From 282b06949e8eed6dae2790846d3a2036939f9cd8 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Fri, 19 Jun 2026 01:33:13 -0700 Subject: [PATCH] FN-6688: replace dashboard primary text alias Replace undefined dashboard primary text aliases with the canonical theme-aware text token. - Swap non-Command-Center CSS references from `--text-primary` to `--text` across dashboard components. - Extend text token canonicalization tests to block future `--text-primary` usage and root alias definitions. - Document the dashboard CSS token rule and add a patch changeset for the published CLI bundle. Files changed: .changeset/fn-6688-text-primary-cleanup.md | 5 +++++ docs/dashboard-guide.md | 2 ++ .../__tests__/text-token-canonicalization.test.ts | 18 ++++++++++++++++-- .../dashboard/app/components/ActiveAgentsPanel.css | 4 ++-- .../dashboard/app/components/CliBinaryPanel.css | 2 +- .../app/components/ConversationHistory.css | 6 +++--- .../dashboard/app/components/DesktopLaunchGate.css | 2 +- packages/dashboard/app/components/ErrorBoundary.css | 2 +- .../dashboard/app/components/LanguageSelector.css | 2 +- packages/dashboard/app/components/MobileNavBar.css | 4 ++-- packages/dashboard/app/components/QuickEntryBox.css | 6 +++--- packages/dashboard/app/components/ScriptsModal.css | 21 +++++++++++++-------- .../dashboard/app/components/SkillMultiselect.css | 2 +- .../dashboard/app/components/TaskFieldsSection.css | 4 ++-- .../app/components/WorkflowFieldsPanel.css | 2 +- packages/dashboard/app/styles.css | 2 +- 16 files changed, 55 insertions(+), 29 deletions(-) Fusion-Task-Id: FN-6688 Fusion-Task-Lineage: 96e47af2-8bf5-4326-be24-2816530fd646 --- .changeset/fn-6688-text-primary-cleanup.md | 5 +++++ docs/dashboard-guide.md | 2 ++ .../text-token-canonicalization.test.ts | 18 ++++++++++++++-- .../app/components/ActiveAgentsPanel.css | 4 ++-- .../app/components/CliBinaryPanel.css | 2 +- .../app/components/ConversationHistory.css | 6 +++--- .../app/components/DesktopLaunchGate.css | 2 +- .../app/components/ErrorBoundary.css | 2 +- .../app/components/LanguageSelector.css | 2 +- .../dashboard/app/components/MobileNavBar.css | 4 ++-- .../app/components/QuickEntryBox.css | 6 +++--- .../dashboard/app/components/ScriptsModal.css | 21 ++++++++++++------- .../app/components/SkillMultiselect.css | 2 +- .../app/components/TaskFieldsSection.css | 4 ++-- .../app/components/WorkflowFieldsPanel.css | 2 +- packages/dashboard/app/styles.css | 2 +- 16 files changed, 55 insertions(+), 29 deletions(-) create mode 100644 .changeset/fn-6688-text-primary-cleanup.md diff --git a/.changeset/fn-6688-text-primary-cleanup.md b/.changeset/fn-6688-text-primary-cleanup.md new file mode 100644 index 0000000000..eaed6a36ce --- /dev/null +++ b/.changeset/fn-6688-text-primary-cleanup.md @@ -0,0 +1,5 @@ +--- +"@runfusion/fusion": patch +--- + +Replace non-Command-Center dashboard CSS references to the undefined `--text-primary` alias with the canonical `--text` token so primary text uses the intended theme-aware color. diff --git a/docs/dashboard-guide.md b/docs/dashboard-guide.md index 999f41ccdc..012f923d5a 100644 --- a/docs/dashboard-guide.md +++ b/docs/dashboard-guide.md @@ -1257,6 +1257,8 @@ The `index.html` shell is templated server-side: the server injects a per-user ` Command Center chart surfaces are a stricter token-only zone: `CommandCenter.css`, `areas/areas.css`, and `charts/charts.css` should avoid raw color fallbacks and hardcoded dimensions in component rules, keep secondary copy on `--text-muted`, use canonical `--accent` / `--text` for generic accent and primary text styling, use `--duration-*` for animation durations, and encode mobile chart invariants with shared classes rather than one-off area styles. The undefined `--color-accent` / `--text-primary` aliases are forbidden under `components/command-center/**` and guarded by `command-center-css-token-canonicalization.test.ts`. +Non-Command-Center dashboard CSS uses `--text` as the canonical primary text token. The undefined `--text-primary` alias is forbidden outside `components/command-center/**` and guarded by `packages/dashboard/app/__tests__/text-token-canonicalization.test.ts`. + ### Theme system Dark/light modes via `data-theme`; 54 color themes via `data-color-theme` (lazy-loaded from `app/public/theme-data.css`). diff --git a/packages/dashboard/app/__tests__/text-token-canonicalization.test.ts b/packages/dashboard/app/__tests__/text-token-canonicalization.test.ts index 614568ea7b..e136e559d8 100644 --- a/packages/dashboard/app/__tests__/text-token-canonicalization.test.ts +++ b/packages/dashboard/app/__tests__/text-token-canonicalization.test.ts @@ -1,4 +1,4 @@ -// Regression guard for FN-4286 follow-up to FN-4195: prevent reintroducing undefined --text-secondary. +// Regression guard for FN-4286/FN-6688: prevent reintroducing undefined primary/secondary text aliases. import { readFileSync, readdirSync, statSync } from "node:fs"; import path from "node:path"; import { describe, expect, it } from "vitest"; @@ -38,13 +38,27 @@ describe("text token canonicalization", () => { expect(offenders, `Unexpected --text-secondary references in: ${offenders.join(", ")}`).toEqual([]); }); - it("defines canonical text tokens and does not define --text-secondary at :root", () => { + it("keeps --text-primary out of dashboard source files outside command-center", () => { + const offenders: string[] = []; + for (const relPath of collectSourceFiles(APP_ROOT)) { + if (relPath.startsWith("components/command-center/")) continue; + if (ALLOWLIST.has(relPath)) continue; + const content = readFileSync(path.join(APP_ROOT, relPath), "utf8"); + if (content.includes("--text-primary")) offenders.push(relPath); + } + + expect(offenders, `Unexpected --text-primary references in: ${offenders.join(", ")}`).toEqual([]); + }); + + it("defines canonical text tokens and does not define legacy text aliases at :root", () => { const stylesCss = loadStylesCss(); const rootBlocks = [...stylesCss.matchAll(/:root\s*\{([\s\S]*?)\}/g)].map((match) => match[1]); expect(rootBlocks.length).toBeGreaterThan(0); const allRootContent = rootBlocks.join("\n"); + expect(allRootContent).not.toMatch(/^\s*--text-primary\s*:/m); expect(allRootContent).not.toMatch(/^\s*--text-secondary\s*:/m); + expect(allRootContent).toMatch(/^\s*--text\s*:/m); expect(allRootContent).toMatch(/^\s*--text-muted\s*:/m); expect(allRootContent).toMatch(/^\s*--text-dim\s*:/m); }); diff --git a/packages/dashboard/app/components/ActiveAgentsPanel.css b/packages/dashboard/app/components/ActiveAgentsPanel.css index e8014c9e08..bf56321747 100644 --- a/packages/dashboard/app/components/ActiveAgentsPanel.css +++ b/packages/dashboard/app/components/ActiveAgentsPanel.css @@ -79,7 +79,7 @@ .live-agent-card-status { font-style: normal; font-weight: 500; - color: var(--text-primary); + color: var(--text); opacity: 1; white-space: nowrap; overflow: hidden; @@ -153,7 +153,7 @@ .live-agent-card-logs-btn:hover { background: var(--bg-hover); - color: var(--text-primary); + color: var(--text); border-color: var(--border-strong, var(--border)); } diff --git a/packages/dashboard/app/components/CliBinaryPanel.css b/packages/dashboard/app/components/CliBinaryPanel.css index 3af1959587..79a501f4a6 100644 --- a/packages/dashboard/app/components/CliBinaryPanel.css +++ b/packages/dashboard/app/components/CliBinaryPanel.css @@ -185,7 +185,7 @@ flex: 1; font-family: var(--font-mono, ui-monospace, "SFMono-Regular", monospace); font-size: 12.5px; - color: var(--text-primary, #e6edf3); + color: var(--text); white-space: nowrap; overflow-x: auto; } diff --git a/packages/dashboard/app/components/ConversationHistory.css b/packages/dashboard/app/components/ConversationHistory.css index 9c6a4c1b0b..94411c5622 100644 --- a/packages/dashboard/app/components/ConversationHistory.css +++ b/packages/dashboard/app/components/ConversationHistory.css @@ -34,7 +34,7 @@ border-radius: 999px; background: color-mix(in srgb, var(--bg-secondary) 80%, transparent); border: 1px solid var(--border-primary); - color: var(--text-primary); + color: var(--text); font-size: 11px; font-weight: 600; letter-spacing: 0.02em; @@ -46,7 +46,7 @@ border-radius: 8px; background: var(--bg-secondary); padding: 8px 10px; - color: var(--text-primary); + color: var(--text); display: flex; flex-direction: column; gap: 4px; @@ -99,7 +99,7 @@ .conversation-thinking-toggle:hover { background: var(--bg-secondary); - color: var(--text-primary); + color: var(--text); } .conversation-thinking-toggle:focus-visible { diff --git a/packages/dashboard/app/components/DesktopLaunchGate.css b/packages/dashboard/app/components/DesktopLaunchGate.css index b901e3816e..c951c668ba 100644 --- a/packages/dashboard/app/components/DesktopLaunchGate.css +++ b/packages/dashboard/app/components/DesktopLaunchGate.css @@ -5,7 +5,7 @@ align-items: center; justify-content: center; background: var(--bg-primary, #0f1117); - color: var(--text-primary, #e5e7eb); + color: var(--text); z-index: 9999; padding: 1.5rem; } diff --git a/packages/dashboard/app/components/ErrorBoundary.css b/packages/dashboard/app/components/ErrorBoundary.css index 4835fab791..a70b2c6ee4 100644 --- a/packages/dashboard/app/components/ErrorBoundary.css +++ b/packages/dashboard/app/components/ErrorBoundary.css @@ -11,7 +11,7 @@ max-width: 480px; margin: auto; text-align: center; - color: var(--text-primary); + color: var(--text); } .error-boundary--root { diff --git a/packages/dashboard/app/components/LanguageSelector.css b/packages/dashboard/app/components/LanguageSelector.css index c6d8a8a92c..6ea675059c 100644 --- a/packages/dashboard/app/components/LanguageSelector.css +++ b/packages/dashboard/app/components/LanguageSelector.css @@ -25,7 +25,7 @@ border: 1px solid var(--border-color, #333); border-radius: 6px; background: var(--bg-secondary, transparent); - color: var(--text-primary, inherit); + color: var(--text); font-size: 0.875rem; cursor: pointer; transition: border-color 0.15s ease, background 0.15s ease; diff --git a/packages/dashboard/app/components/MobileNavBar.css b/packages/dashboard/app/components/MobileNavBar.css index 2ead65917e..c6831588ea 100644 --- a/packages/dashboard/app/components/MobileNavBar.css +++ b/packages/dashboard/app/components/MobileNavBar.css @@ -96,7 +96,7 @@ } .mobile-nav-tab:active { - color: var(--text-primary); + color: var(--text); } .mobile-nav-tab--active { @@ -218,7 +218,7 @@ min-height: 36px; background: none; border: none; - color: var(--text-primary); + color: var(--text); font-size: 15px; cursor: pointer; transition: background 0.15s ease; diff --git a/packages/dashboard/app/components/QuickEntryBox.css b/packages/dashboard/app/components/QuickEntryBox.css index bf947e022d..a19682340e 100644 --- a/packages/dashboard/app/components/QuickEntryBox.css +++ b/packages/dashboard/app/components/QuickEntryBox.css @@ -236,7 +236,7 @@ background: none; border: none; border-radius: 6px; - color: var(--text-primary); + color: var(--text); cursor: pointer; text-align: left; font-size: 0.8125rem; @@ -289,13 +289,13 @@ } .model-submenu-back:hover { - color: var(--text-primary); + color: var(--text); } .model-submenu-header { font-size: 0.75rem; font-weight: 600; - color: var(--text-primary); + color: var(--text); padding-bottom: 4px; border-bottom: 1px solid var(--border); } diff --git a/packages/dashboard/app/components/ScriptsModal.css b/packages/dashboard/app/components/ScriptsModal.css index 332ad44312..2232e54fd5 100644 --- a/packages/dashboard/app/components/ScriptsModal.css +++ b/packages/dashboard/app/components/ScriptsModal.css @@ -1,3 +1,8 @@ +/* +FNXC:DashboardStyling 2026-06-19-00:00: +Non-Command-Center dashboard CSS must use the canonical --text token. The legacy primary-text alias was undefined, so it made primary text inherit fallback/parent colors or hardcoded hex colors that ignored the active theme (FN-6688). +*/ + /* === Scripts Modal === */ .scripts-modal { display: flex; @@ -258,7 +263,7 @@ .schedule-empty-state h4 { margin: 0; font-size: 16px; - color: var(--text-primary); + color: var(--text); } .schedule-empty-state p { @@ -317,7 +322,7 @@ .schedule-card-name { font-weight: 600; font-size: 14px; - color: var(--text-primary); + color: var(--text); } .schedule-type-badge { @@ -441,7 +446,7 @@ } .schedule-history-toggle:hover { - color: var(--text-primary); + color: var(--text); } .schedule-history-list { @@ -597,7 +602,7 @@ .schedule-step-result-name { font-weight: 500; - color: var(--text-primary); + color: var(--text); } .schedule-step-result-error { @@ -695,7 +700,7 @@ flex: 1; font-size: 12px; font-weight: 500; - color: var(--text-primary); + color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -865,7 +870,7 @@ .routine-empty-state h4 { margin: 0; font-size: 16px; - color: var(--text-primary); + color: var(--text); } .routine-empty-state p { @@ -934,7 +939,7 @@ .routine-card-name { font-weight: 600; font-size: 14px; - color: var(--text-primary); + color: var(--text); } .routine-trigger-badge { @@ -1113,7 +1118,7 @@ .routine-trigger-btn:hover { border-color: var(--border-hover, var(--border)); background: var(--bg-secondary); - color: var(--text-primary); + color: var(--text); } .routine-trigger-btn.active { diff --git a/packages/dashboard/app/components/SkillMultiselect.css b/packages/dashboard/app/components/SkillMultiselect.css index b50241c64a..2540670e3a 100644 --- a/packages/dashboard/app/components/SkillMultiselect.css +++ b/packages/dashboard/app/components/SkillMultiselect.css @@ -54,7 +54,7 @@ } .skill-chip-remove:hover:not(:disabled) { - color: var(--text-primary); + color: var(--text); background: var(--bg-hover); } diff --git a/packages/dashboard/app/components/TaskFieldsSection.css b/packages/dashboard/app/components/TaskFieldsSection.css index 59798e3b3a..cce33d7786 100644 --- a/packages/dashboard/app/components/TaskFieldsSection.css +++ b/packages/dashboard/app/components/TaskFieldsSection.css @@ -41,7 +41,7 @@ border: 1px solid var(--border-color, #2a2d34); border-radius: 6px; background: var(--input-bg, #16181d); - color: var(--text-primary, #e6e6e6); + color: var(--text); font-size: 13px; font-family: inherit; } @@ -103,7 +103,7 @@ align-items: center; gap: 6px; font-size: 13px; - color: var(--text-primary, #e6e6e6); + color: var(--text); cursor: pointer; } diff --git a/packages/dashboard/app/components/WorkflowFieldsPanel.css b/packages/dashboard/app/components/WorkflowFieldsPanel.css index 9c130973d9..6cc9b74770 100644 --- a/packages/dashboard/app/components/WorkflowFieldsPanel.css +++ b/packages/dashboard/app/components/WorkflowFieldsPanel.css @@ -170,7 +170,7 @@ } .wf-field-color-swatch.is-active { - outline: 2px solid var(--text-primary, #fff); + outline: 2px solid var(--text); outline-offset: 1px; } diff --git a/packages/dashboard/app/styles.css b/packages/dashboard/app/styles.css index 342c36da59..95b4e43ad6 100644 --- a/packages/dashboard/app/styles.css +++ b/packages/dashboard/app/styles.css @@ -1258,7 +1258,7 @@ body { } .modal-send-to-background:hover { - color: var(--text-primary); + color: var(--text); } .modal-send-to-background:focus-visible {