FN-7681: Fix undefined --space-2xs token references in dashboard component CSS

Replaces undefined --space-2xs CSS custom property references with the smallest defined spacing token, --space-xs, across several dashboard component stylesheets, and hardens the regression test that guards against reintroducing --space-2xs by scanning components recursively.

- Replace var(--space-2xs) with var(--space-xs) in NewTaskModal.css, QuickEntryBox.css, TaskReviewTab.css, and McpServersCard.css
- Update QuickEntryBox.test.tsx assertion to expect --space-xs instead of --space-2xs
- Make space-token-defined.test.ts recursively walk components directory (previously only scanned the top level, missing nested files like settings/sections/McpServersCard.css)
- Add FNXC:DashboardTokens comments documenting the --space-2xs is-intentionally-undefined decision (FN-5934) and the recursive-scan fix rationale (FN-7681)

Files changed:
 .../app/__tests__/space-token-defined.test.ts      | 33 ++++++++++++++++++----
 packages/dashboard/app/components/NewTaskModal.css |  2 +-
 .../dashboard/app/components/QuickEntryBox.css     | 18 ++++++++----
 .../dashboard/app/components/TaskReviewTab.css     |  2 +-
 .../components/__tests__/QuickEntryBox.test.tsx    |  2 +-
 .../settings/sections/McpServersCard.css           |  4 +--
 6 files changed, 44 insertions(+), 17 deletions(-)

Fusion-Task-Id: FN-7681

Fusion-Task-Lineage: 59850d5c-09cc-4178-ac45-9792fa9aca59

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-07-08 09:19:42 -07:00
parent a8c018f7d4
commit 5c0ed62001
6 changed files with 44 additions and 17 deletions

View File

@@ -7,24 +7,45 @@ const componentsDir = resolve(appDir, "components");
const stylesPath = resolve(appDir, "styles.css");
const themeDataPath = resolve(appDir, "public/theme-data.css");
/*
* FNXC:DashboardTokens 2026-07-08-00:00:
* FN-7681 found --space-2xs references escaping this guard inside a
* subdirectory stylesheet (settings/sections/McpServersCard.css) because the
* original scan was non-recursive. Walk componentsDir recursively (bounded,
* no temp-root traversal) so every component CSS file is covered, while
* skipping __tests__ directories and non-.css files.
*/
function listComponentCssFiles(): string[] {
return readdirSync(componentsDir)
.filter((name) => name.endsWith(".css"))
.sort();
const results: string[] = [];
function walk(dir: string): void {
for (const entry of readdirSync(dir, { withFileTypes: true })) {
if (entry.name === "__tests__") continue;
const entryPath = join(dir, entry.name);
if (entry.isDirectory()) {
walk(entryPath);
} else if (entry.isFile() && entry.name.endsWith(".css")) {
results.push(entryPath);
}
}
}
walk(componentsDir);
return results.sort();
}
describe("dashboard spacing token hygiene", () => {
it("does not reference undefined --space-2xs in any component stylesheet", () => {
const violations: string[] = [];
for (const fileName of listComponentCssFiles()) {
const filePath = join(componentsDir, fileName);
for (const filePath of listComponentCssFiles()) {
const relativePath = filePath.slice(componentsDir.length + 1);
const source = readFileSync(filePath, "utf8");
const lines = source.split(/\r?\n/);
for (let index = 0; index < lines.length; index += 1) {
if (lines[index].includes("var(--space-2xs)")) {
violations.push(`${fileName}:${index + 1}:${lines[index].trim()}`);
violations.push(`${relativePath}:${index + 1}:${lines[index].trim()}`);
}
}
}

View File

@@ -492,7 +492,7 @@ The New Task create workflow selector is a real dropdown so workflow identity ic
.task-workflow-option-copy {
display: flex;
flex-direction: column;
gap: var(--space-2xs);
gap: var(--space-xs);
min-width: 0;
flex: 1 1 auto;
}

View File

@@ -12,9 +12,15 @@
border-color: var(--todo);
}
/*
FNXC:DashboardTokens 2026-07-08-00:00:
--space-2xs is intentionally undefined (FN-5934 decision); the guard in
app/__tests__/space-token-defined.test.ts asserts it stays undefined. Use the
smallest defined token, --space-xs (4px), instead of reintroducing --space-2xs.
*/
.quick-entry-box--drag-over {
border-color: var(--triage);
box-shadow: 0 0 0 var(--space-2xs) color-mix(in srgb, var(--triage) 35%, transparent);
box-shadow: 0 0 0 var(--space-xs) color-mix(in srgb, var(--triage) 35%, transparent);
}
.quick-entry-drop-target {
@@ -26,7 +32,7 @@
justify-content: center;
gap: var(--space-xs);
padding: var(--space-sm);
border: var(--space-2xs) dashed var(--triage);
border: var(--space-xs) dashed var(--triage);
border-radius: var(--radius-sm);
background: color-mix(in srgb, var(--card) 86%, var(--triage));
color: var(--text);
@@ -38,12 +44,12 @@
.quick-entry-attach-button {
position: relative;
min-width: calc(var(--space-xl) + var(--space-xs));
gap: var(--space-2xs);
gap: var(--space-xs);
}
.quick-entry-attach-count {
min-width: var(--space-md);
padding: 0 var(--space-2xs);
padding: 0 var(--space-xs);
border-radius: calc(var(--radius) * 4);
background: var(--triage);
color: var(--provider-icon-contrast);
@@ -54,7 +60,7 @@
@media (max-width: 768px) {
.quick-entry-drop-target {
inset: var(--space-2xs);
inset: var(--space-xs);
padding: var(--space-xs);
text-align: center;
}
@@ -288,7 +294,7 @@ FN-7677 — the workflow trigger shares `.btn.btn-sm.dep-trigger` classes with o
.quick-entry-workflow-option-copy {
display: flex;
flex-direction: column;
gap: var(--space-2xs);
gap: var(--space-xs);
min-width: 0;
}

View File

@@ -248,7 +248,7 @@ The Review tab mirrors the Import-from-GitHub comment provenance model: avatar,
.task-review-tab__comment-type-badge {
display: inline-flex;
align-items: center;
gap: var(--space-2xs);
gap: var(--space-xs);
padding: 0 var(--space-xs);
min-height: var(--space-lg);
border-radius: var(--radius-pill);

View File

@@ -1780,7 +1780,7 @@ describe("QuickEntryBox", () => {
expect(menuRule).not.toContain("inset-inline-start: 0");
expect(menuRule).toContain("width: min(calc(var(--space-xl) * 16), calc(100vw - var(--space-lg)))");
expect(menuRule).toContain("min-width: min(calc(var(--space-xl) * 14), calc(100vw - var(--space-lg)))");
expect(optionCopyRule).toContain("gap: var(--space-2xs)");
expect(optionCopyRule).toContain("gap: var(--space-xs)");
expect(optionNameRule).toContain("overflow-wrap: anywhere");
expect(optionNameRule).toContain("white-space: normal");
expect(optionNameRule).not.toContain("text-overflow: ellipsis");

View File

@@ -86,7 +86,7 @@
display: inline-flex;
align-items: center;
border-radius: var(--radius-full);
padding: var(--space-2xs) var(--space-xs);
padding: var(--space-xs) var(--space-xs);
font-size: var(--font-size-xs);
line-height: var(--line-height-tight);
border: var(--border-width) solid var(--border);
@@ -128,7 +128,7 @@
display: inline-flex;
align-items: center;
border-radius: var(--radius-full);
padding: var(--space-2xs) var(--space-xs);
padding: var(--space-xs) var(--space-xs);
border: var(--border-width) solid var(--border);
color: inherit;
}