feat(lint): make contrast check blocking + fix dark subtle-text AA
Per review on #7009: the contrast check was warn-only and opt-in, so it would never catch regressions. Enforce it here (the follow-up PR): - theme-lint.mjs `contrast` mode now exits non-zero on any sub-floor pair - wired into `task frontend:lint:colors` (runs in the blocking lint gate) - fixed the two failing pairs: --c-text-subtle on dark/portal-dark surfaces (3.36/3.67 → 4.74/5.18) by adding --p-zinc-250 and pointing dark subtle at it All text-on-surface / on-primary pairs now clear WCAG AA per theme.
This commit is contained in:
@@ -212,9 +212,10 @@ tasks:
|
||||
- node editor/scripts/lint/theme-lint.mjs
|
||||
- node editor/scripts/lint/theme-lint.mjs css-colors
|
||||
- node editor/scripts/lint/theme-lint.mjs code-colors
|
||||
- node editor/scripts/lint/theme-lint.mjs contrast
|
||||
|
||||
contrast:
|
||||
desc: "Report low-contrast theme token pairs (warning only, never blocks)"
|
||||
desc: "Check theme token pairs clear WCAG AA contrast per theme"
|
||||
deps: [install]
|
||||
cmds:
|
||||
- node editor/scripts/lint/theme-lint.mjs contrast
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
// contexts; `// theme-allow-color` opt-out;
|
||||
// exempt PATHS for rendering/vendor/config).
|
||||
// Scope: all editor/src. (blocking)
|
||||
// node theme-lint.mjs contrast warn-only WCAG contrast report (never blocks)
|
||||
// node theme-lint.mjs contrast enforce: text-on-surface / on-primary pairs
|
||||
// clear WCAG AA per theme (default accent).
|
||||
// (blocking)
|
||||
//
|
||||
// Structural black / white / transparent (shadows, scrims) are always allowed.
|
||||
|
||||
@@ -307,9 +309,10 @@ function reportContrast() {
|
||||
}
|
||||
console.log(
|
||||
warnings
|
||||
? `⚠ ${warnings} pair(s) below floor — review, not blocking.`
|
||||
? `✖ ${warnings} pair(s) below floor.`
|
||||
: "✓ all pairs clear their floor.",
|
||||
);
|
||||
return warnings;
|
||||
}
|
||||
|
||||
// ── css-colors (blocking): no hardcoded colour in ANY source .css ────────────
|
||||
@@ -453,8 +456,14 @@ function checkCodeColors() {
|
||||
|
||||
// ── CLI ──────────────────────────────────────────────────────────────────────
|
||||
if (process.argv.includes("contrast")) {
|
||||
reportContrast();
|
||||
process.exit(0); // never blocks
|
||||
const below = reportContrast();
|
||||
if (below) {
|
||||
console.error(
|
||||
`\nRaise the failing token's lightness (or its surface's) until every pair clears WCAG AA (4.5 normal / 3.0 on-primary).\n`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (process.argv.includes("code-colors")) {
|
||||
|
||||
@@ -63,9 +63,9 @@ One file: `editor/scripts/lint/theme-lint.mjs` (no baseline). Run via `task fron
|
||||
- **Default (blocking):** enforces "literals only in `primitives.css`; everything else in `core/theme/` references tokens; no duplicate primitives." Scope: `core/theme/`.
|
||||
- **`css-colors` (blocking):** enforces **no hardcoded colour in any source `.css`** across `editor/src` — `primitives.css` (the literal home) and generated `output.css` are the only exemptions. The file list comes from `git ls-files` (not a directory walk), and comments/structural black-white-transparent are ignored.
|
||||
- **`code-colors` (blocking):** enforces **no hardcoded colour in TS/TSX DOM code**. TS/TSX can't be zeroed like CSS (canvas/PDF/pdfium rendering, colour maths, colour pickers, vendor brand and self-contained docs legitimately need numeric colour), so it's **default-deny with layered exemptions**: (1) structural black/white/transparent; (2) detected safe contexts on the line — `var(--x, …)` / `readColor(…)` fallbacks, canvas assignments (`fillStyle`/`ctx.`…), pdf-lib `rgb(0..1)`, and an explicit **`// theme-allow-color <reason>`** opt-out; (3) exempt **paths** for whole rendering/vendor/config/illustration areas (viewer/annotation, `*Thumbnail`/`*Overlay`, `*Pdf*`, colour pickers, `onboarding/`, `mantineTheme`/`theme.ts`, tests, stories, type decls, marketing banners). A raw colour anywhere else fails. Adding a new colour to a normal component → blocked; genuine exceptions get the marker.
|
||||
- **`node theme-lint.mjs contrast`** (task `frontend:contrast`, non-blocking): WCAG contrast report for text-on-surface / on-primary pairs per theme.
|
||||
- **`contrast` (blocking):** fails if any text-on-surface / on-primary pair misses its WCAG AA floor (4.5 normal, 3.0 on-primary) in any theme, resolved for the default accent. Custom accents are handled at runtime by the `customPrimary.ts` guardrails.
|
||||
|
||||
All three blocking modes run under `task frontend:lint:colors`. Source **CSS is at zero** hardcoded colour and **TS/TSX DOM code is clean** (only the exempt rendering/vendor/config areas hold literals). Keep it there — add the literal to `primitives.css` and reference `var(--p-…)`, or for an unavoidable TS/TSX literal add `// theme-allow-color <reason>`.
|
||||
All four blocking modes run under `task frontend:lint:colors`. Source **CSS is at zero** hardcoded colour and **TS/TSX DOM code is clean** (only the exempt rendering/vendor/config areas hold literals). Keep it there — add the literal to `primitives.css` and reference `var(--p-…)`, or for an unavoidable TS/TSX literal add `// theme-allow-color <reason>`.
|
||||
|
||||
## Gotchas
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ html[data-app-theme="midnight"] {
|
||||
|
||||
--c-text: var(--p-zinc-100);
|
||||
--c-text-muted: var(--p-zinc-200);
|
||||
--c-text-subtle: var(--p-zinc-300);
|
||||
--c-text-subtle: var(--p-zinc-250);
|
||||
--c-text-on-primary: var(--p-white);
|
||||
|
||||
--c-border: var(--p-zinc-650);
|
||||
@@ -209,7 +209,7 @@ html[data-app-theme="custom"][data-mantine-color-scheme="dark"] {
|
||||
/* Neutral text / borders / overlay (not accent-tinted). */
|
||||
--c-text: var(--p-zinc-100);
|
||||
--c-text-muted: var(--p-zinc-200);
|
||||
--c-text-subtle: var(--p-zinc-300);
|
||||
--c-text-subtle: var(--p-zinc-250);
|
||||
--c-border-strong: var(--p-zinc-500);
|
||||
--c-overlay: rgba(0, 0, 0, 0.6);
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
--p-zinc-500: #3f3f46;
|
||||
--p-zinc-400: #52525b;
|
||||
--p-zinc-300: #71717a;
|
||||
--p-zinc-250: #8a8a93;
|
||||
--p-zinc-200: #a1a1aa;
|
||||
--p-zinc-100: #f4f4f5;
|
||||
--p-blue-400: #60a5fa;
|
||||
|
||||
Reference in New Issue
Block a user