From df3d9ee44f2a965ebac4b7604a25445cccf69027 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Fri, 3 Jul 2026 18:14:32 +0800 Subject: [PATCH] refactor(ui): land type-scale drift guard on the feature branch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ports the type-scale drift diagnostic (from the unmerged claude/type-scale-display-tokens line) onto feature/tools-page-mockups so the M3 token-adoption backlog can't silently regrow. Item 1 of the token-adoption audit follow-ups (docs/redesign/07-token-adoption-audit.md). - scripts/check-type-scale.mjs: report-only diagnostic (npm run check:type-scale) flagging arbitrary text-[px|rem|em] font sizes that bypass the @theme scale; --strict exits non-zero for a future CI gate. Colour utilities text-[color:var(--…)] are intentionally not flagged. - globals.css: add size-only display tokens --text-lg-minus (17px) and --text-2xl-minus (22px), completing the scale's display tail. - package.json: wire check:type-scale into verify:cheap in REPORT mode (non-breaking). Promote to --strict once the 111-item backlog is cleared (audit item 2). Deliberately excludes the three component call-site swaps to avoid colliding with in-flight WIP; those belong with the item-2 backlog cleanup. Co-Authored-By: Claude Opus 4.8 --- package.json | 3 +- scripts/check-type-scale.mjs | 62 ++++++++++++++++++++++++++++++++++++ src/app/globals.css | 7 +++- 3 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 scripts/check-type-scale.mjs diff --git a/package.json b/package.json index cbfb2f301b..079592b3b9 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "test:e2e:accessibility": "node scripts/run-playwright.mjs tests/ui-accessibility.spec.ts --project=chromium", "test:e2e:chromium": "node scripts/run-playwright.mjs --project=chromium", "test:e2e:visual": "node scripts/run-playwright.mjs --config=playwright.visual.config.ts", - "verify:cheap": "npm run check:runtime && npm run lint && npm run typecheck && npm run test", + "verify:cheap": "npm run check:runtime && npm run lint && npm run typecheck && npm run test && npm run check:type-scale", "verify:ui": "npm run check:runtime && npm run test:e2e:chromium", "verify:release": "npm run check:runtime && npm run lint && npm run typecheck && npm run test && npm run build && npm run test:e2e && npm run check:production-readiness && npm run governance:release && npm run eval:quality:release", "ci:env-check": "node scripts/check-ci-env.mjs", @@ -53,6 +53,7 @@ "backfill:text-normalization": "tsx scripts/backfill-text-normalization.ts", "check:supabase-project": "tsx scripts/check-supabase-project.ts", "check:indexing": "tsx scripts/check-indexing.ts", + "check:type-scale": "node scripts/check-type-scale.mjs", "recover:ingestion": "tsx scripts/recover-ingestion-queue.ts", "registry:seed": "tsx scripts/seed-registry-records.ts", "reindex": "tsx scripts/reindex.ts", diff --git a/scripts/check-type-scale.mjs b/scripts/check-type-scale.mjs new file mode 100644 index 0000000000..9276d487fe --- /dev/null +++ b/scripts/check-type-scale.mjs @@ -0,0 +1,62 @@ +#!/usr/bin/env node +// Guardrail/diagnostic: flags arbitrary Tailwind font-size utilities +// (e.g. text-[12px], text-[1.45rem]) that bypass the design type scale. +// +// The scale lives in the @theme block of src/app/globals.css: named steps +// text-4xs … text-2xl-minus, on top of Tailwind's default xs/sm/base/lg/xl/2xl. +// Arbitrary text-[] values re-introduce off-scale sizes; this check +// tracks that drift. Colour utilities (text-[color:var(--…)]) are the sanctioned +// token-access form and are intentionally NOT flagged. +// +// Usage: +// node scripts/check-type-scale.mjs report only, exit 0 (default) +// node scripts/check-type-scale.mjs --strict exit 1 if any found (promote to a +// CI gate once the backlog is cleared) + +import { readFileSync } from "node:fs"; +import { execSync } from "node:child_process"; + +const strict = process.argv.includes("--strict"); +const ARBITRARY = /\btext-\[(\d*\.?\d+)(px|rem|em)\]/g; + +// Tracked source files under src (fast; respects .gitignore). +const files = execSync("git ls-files src", { encoding: "utf8" }) + .split("\n") + .filter((f) => /\.(tsx?|jsx?|css)$/.test(f)); + +const hits = []; +for (const file of files) { + let text; + try { + text = readFileSync(file, "utf8"); + } catch { + continue; + } + text.split("\n").forEach((line, i) => { + for (const m of line.matchAll(ARBITRARY)) { + hits.push({ file, line: i + 1, match: m[0] }); + } + }); +} + +if (hits.length === 0) { + console.log("✓ type-scale: no arbitrary text-[px|rem|em] font sizes in src."); + process.exit(0); +} + +const bySize = new Map(); +for (const h of hits) bySize.set(h.match, (bySize.get(h.match) ?? 0) + 1); +const fileCount = new Set(hits.map((h) => h.file)).size; + +console.log(`type-scale: ${hits.length} arbitrary font-size utilities bypass the scale (across ${fileCount} files):\n`); +for (const [size, n] of [...bySize.entries()].sort((a, b) => b[1] - a[1])) { + console.log(` ${String(n).padStart(4)} ${size}`); +} +console.log("\nPrefer a named @theme step from src/app/globals.css (text-3xs, text-sm-minus, text-2xl-minus, …)."); +console.log("Colour utilities like text-[color:var(--…)] are fine and not counted."); + +if (strict) { + console.error("\n✗ --strict: arbitrary font sizes present."); + process.exit(1); +} +process.exit(0); diff --git a/src/app/globals.css b/src/app/globals.css index 7fc0f91903..c2e1c148ae 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -38,12 +38,17 @@ 2xs 11px small chips / secondary labels (most common small size) (xs 12 · sm 14 · base 16 come from Tailwind's default scale) sm-minus 13px one notch under sm — dense body/labels bigger than xs - base-minus 15px one notch under base — comfortable body just under 16 */ + base-minus 15px one notch under base — comfortable body just under 16 + (lg 18 · xl 20 · 2xl 24 come from Tailwind's default scale) + lg-minus 17px one notch under lg — display titles a shade under 18 + 2xl-minus 22px one notch under 2xl — wide-screen nowrap headings */ --text-4xs: 0.5rem; --text-3xs: 0.625rem; --text-2xs: 0.6875rem; --text-sm-minus: 0.8125rem; --text-base-minus: 0.9375rem; + --text-lg-minus: 1.0625rem; + --text-2xl-minus: 1.375rem; /* Font families: bind Tailwind's font-sans / font-mono to the loaded Geist faces (variables set on by next/font). font-mono is used for