From 0e7e640062d123a6f58799735fc51e03e78f778f Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Thu, 20 Aug 2026 10:59:28 -0700 Subject: [PATCH] fix(ui): move the policy flow builder onto theme tokens (#37654) * fix(ui): move the policy flow builder onto theme tokens The flow builder carried its own private palette: 126 raw literals across a 1644-line file, hardcoded into React inline style objects and SVG presentation attributes. Inline styles beat every class, so the whole page, its version sidebar, its step cards and its test panel stayed light no matter what the theme said. Each literal now resolves through the token it was already imitating. The greys map onto card, muted, border, muted-foreground and foreground; the indigo and blue accents onto info; the pass, fail and API-failure accents onto success, destructive and warning; and the pale status washes become a color-mix of the same token so they track it in both themes. Six icons carried their colour as an SVG presentation attribute, where custom properties do not substitute, so those switch to currentColor with the token set alongside. Light mode is not byte-identical, and that is the point: the file stops keeping a second palette. Of the mappings, card, muted and border land on the exact same rgb they had, covering most of the file. The rest snap to the dashboard's canonical shade, which mostly means slightly darker text and deeper status colours: the gray-400 labels pick up real contrast, the soft red on the fail icon becomes the destructive red every other failure indicator uses, and the indigo accent becomes the blue that info resolves to. Verified in a browser on both themes. In dark mode nothing on the page paints a light background any more; the six that still do are shadcn's inverted primary buttons and badges, which are meant to. * fix(ui): token the flow builder test textarea fill The quick-chat textarea is the one bare form control left in the file, so the @tailwindcss/forms base layer still paints it `background-color: #fff`. The inline style overrode the plugin's border but not its fill, which left a white box inside the now-dark test panel, and its text inherits the near-white foreground, so the typed message was invisible in dark mode. Pin both halves of the pair on the element the plugin styles: the card token it sits on, and the foreground token it was already inheriting. --- .../_components/pipeline_flow_builder.tsx | 374 ++++++++++++------ 1 file changed, 245 insertions(+), 129 deletions(-) diff --git a/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/pipeline_flow_builder.tsx b/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/pipeline_flow_builder.tsx index 4bb87b62243..8af8ac7a414 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/pipeline_flow_builder.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/pipeline_flow_builder.tsx @@ -113,7 +113,7 @@ const GuardrailIcon: React.FC = () => ( width: 28, height: 28, borderRadius: "50%", - backgroundColor: "#eef2ff", + backgroundColor: "color-mix(in oklab, var(--color-info) 10%, transparent)", display: "flex", alignItems: "center", justifyContent: "center", @@ -125,8 +125,9 @@ const GuardrailIcon: React.FC = () => ( height="14" viewBox="0 0 24 24" fill="none" - stroke="#6366f1" + stroke="currentColor" strokeWidth="2" + style={{ color: "var(--color-info)" }} strokeLinecap="round" strokeLinejoin="round" > @@ -142,14 +143,21 @@ const PlayIcon: React.FC = () => ( width: 28, height: 28, borderRadius: "50%", - backgroundColor: "#f3f4f6", + backgroundColor: "var(--color-muted)", display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0, }} > - + @@ -161,11 +169,11 @@ const PassIcon: React.FC = () => ( height="14" viewBox="0 0 24 24" fill="none" - stroke="#22c55e" + stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" - style={{ flexShrink: 0 }} + style={{ flexShrink: 0, color: "var(--color-success)" }} > @@ -178,11 +186,11 @@ const FailIcon: React.FC = () => ( height="14" viewBox="0 0 24 24" fill="none" - stroke="#f87171" + stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" - style={{ flexShrink: 0 }} + style={{ flexShrink: 0, color: "var(--color-destructive)" }} > @@ -194,11 +202,11 @@ const ApiFailureIcon: React.FC = () => ( height="14" viewBox="0 0 24 24" fill="none" - stroke="#d97706" + stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" - style={{ flexShrink: 0 }} + style={{ flexShrink: 0, color: "var(--color-warning)" }} > @@ -216,7 +224,7 @@ interface ConnectorProps { const Connector: React.FC = ({ onInsert }) => (
-
+
-
+
); @@ -275,9 +283,9 @@ const StepCard: React.FC = ({ return (
= ({ fontSize: 11, fontWeight: 700, textTransform: "uppercase", - color: "#6366f1", + color: "var(--color-info)", letterSpacing: "0.06em", }} > @@ -300,7 +308,7 @@ const StepCard: React.FC = ({
- Step {stepIndex + 1} + Step {stepIndex + 1}
{/* Guardrail selector */}
-
{/* ON PASS section */} -
+
- ON PASS + ON PASS
-
@@ -638,15 +707,15 @@ export const PipelineInfoDisplay: React.FC = ({ pipeli {pipeline.steps.map((step, index) => ( {/* Connector */} -
+
{/* Step card */}
= ({ pipeli fontSize: 11, fontWeight: 700, textTransform: "uppercase", - color: "#6366f1", + color: "var(--color-info)", letterSpacing: "0.06em", }} > GUARDRAIL
- Step {index + 1} + Step {index + 1}
{/* Name */} -
{step.guardrail}
+
+ {step.guardrail} +
{/* Divider */} -
+
{/* Pass / Fail / API failure */} -
+
Pass → {ACTION_LABELS[step.on_pass] || step.on_pass} @@ -708,15 +779,27 @@ interface PipelineTestPanelProps { } const OUTCOME_STYLES: Record = { - pass: { bg: "#f0fdf4", color: "#16a34a", label: "PASS" }, - fail: { bg: "#fef2f2", color: "#dc2626", label: "FAIL" }, - error: { bg: "#fffbeb", color: "#d97706", label: "ERROR" }, + pass: { + bg: "color-mix(in oklab, var(--color-success) 10%, transparent)", + color: "var(--color-success)", + label: "PASS", + }, + fail: { + bg: "color-mix(in oklab, var(--color-destructive) 10%, transparent)", + color: "var(--color-destructive)", + label: "FAIL", + }, + error: { + bg: "color-mix(in oklab, var(--color-warning) 10%, transparent)", + color: "var(--color-warning)", + label: "ERROR", + }, }; const TERMINAL_STYLES: Record = { - allow: { bg: "#f0fdf4", color: "#16a34a" }, - block: { bg: "#fef2f2", color: "#dc2626" }, - modify_response: { bg: "#eff6ff", color: "#2563eb" }, + allow: { bg: "color-mix(in oklab, var(--color-success) 10%, transparent)", color: "var(--color-success)" }, + block: { bg: "color-mix(in oklab, var(--color-destructive) 10%, transparent)", color: "var(--color-destructive)" }, + modify_response: { bg: "color-mix(in oklab, var(--color-info) 10%, transparent)", color: "var(--color-info)" }, }; interface ComplianceRunEntry { @@ -801,8 +884,8 @@ const PipelineTestPanel: React.FC = ({ pipeline, accessT
= ({ pipeline, accessT
- Test Pipeline + Test Pipeline
{/* Input section */} -
-