The workspace claims this is enforced. Nothing checks it.
~/dev/stores/DESIGN-SYSTEM.md opens with:
Source of truth. If this file disagrees with a site's CSS, the site is wrong.
and the workspace CLAUDE.md says the shared design system is "Enforced via CI lint + PR
checklist."
For this repo neither half exists. ci.yml:72 runs biome check --error-on-warnings packages workers/mcp — store/ is not in the scan, which is the entire UI (documented honestly in
1587022/#326, so this is a known boundary, not a surprise). And Biome is a JS/TS linter: there is
no stylelint, no token lint, and no vendored tailwind-preset.js in this repo — the preset the
design system says every Tailwind app copies lives only at ~/dev/stores/scripts/tailwind-preset.js.
So the design system is a document the console has never been measured against. Below is what a
first measurement finds. This is the same shape as the defects this batch keeps turning up — a
surface asserting a guarantee nothing behind it enforces.
1. There is no light theme, and no theme mechanism
store/console/src/index.css:14-34 defines one palette, and it is dark:
@theme {
--color-paper: #0a0a0a;
--color-ink: #fafafa;
The design system requires both themes, switched by a data-theme attribute on <html>, with the
preference in localStorage["stores-theme"] so it follows the user between stores. Grepping the
console for data-theme, prefers-color-scheme or stores-theme returns nothing — not in the
CSS, not in the app.
That is a deliberate-looking product decision (a dark developer console) that was never written
down, so it currently reads as 300 lines of unimplemented spec. Either the console is exempt and the
design system should say so, or the light theme is real work. It should not stay ambiguous — the
next person to add a surface cannot tell which rules apply.
It also has live consequences. DataTab.tsx:46,57 sets bg-gray-200 text-gray-600 for a dead
status chip. On a #0a0a0a page that is a light-grey chip with mid-grey text — not a theme bug
waiting to happen, a legibility bug shipping today. 40 raw Tailwind palette classes are in the tree
(text-amber-500 ×8, text-purple-400 ×7, bg-amber-500 ×4, plus violet/orange/gray) against a
standard that says never hardcode these.
2. Status tokens are named for their colour, not their meaning
The console declares --color-green, --color-red, --color-yellow, --color-blue. The design
system declares --danger, --success, --warning, each with a -soft background pairing, and
says explicitly: "Use these for validation, alerts, badges, and state — never hardcode
red/green/yellow."
Naming the token after the pigment defeats the purpose one level up. text-red has 78 uses carrying
at least four unrelated meanings: an error message, a destructive action, the write scope label on
a tool row (SettingsTab.tsx:854), and an offline/failed state. A reader cannot tell which, and a
future decision to make destructive actions orange-not-red becomes 78 case-by-case judgements.
Rename to intent (danger · success · warning · info), add the missing -soft pairings —
several inline bg-red/10 and bg-accent/10 opacity hacks exist precisely because the soft variant
was not available.
3. Nothing measures any of this
Whatever is decided above, it needs a check, or it will drift straight back. Cheapest useful version:
Order matters: land #366's primitives first, then lint — a lint with nowhere to put the fix is
just 168 blocked PRs.
Related: #366 (component primitives), #326 (lint scope), #305.
The workspace claims this is enforced. Nothing checks it.
~/dev/stores/DESIGN-SYSTEM.mdopens with:and the workspace
CLAUDE.mdsays the shared design system is "Enforced via CI lint + PRchecklist."
For this repo neither half exists.
ci.yml:72runsbiome check --error-on-warnings packages workers/mcp—store/is not in the scan, which is the entire UI (documented honestly in1587022/#326, so this is a known boundary, not a surprise). And Biome is a JS/TS linter: there isno stylelint, no token lint, and no vendored
tailwind-preset.jsin this repo — the preset thedesign system says every Tailwind app copies lives only at
~/dev/stores/scripts/tailwind-preset.js.So the design system is a document the console has never been measured against. Below is what a
first measurement finds. This is the same shape as the defects this batch keeps turning up — a
surface asserting a guarantee nothing behind it enforces.
1. There is no light theme, and no theme mechanism
store/console/src/index.css:14-34defines one palette, and it is dark:The design system requires both themes, switched by a
data-themeattribute on<html>, with thepreference in
localStorage["stores-theme"]so it follows the user between stores. Grepping theconsole for
data-theme,prefers-color-schemeorstores-themereturns nothing — not in theCSS, not in the app.
That is a deliberate-looking product decision (a dark developer console) that was never written
down, so it currently reads as 300 lines of unimplemented spec. Either the console is exempt and the
design system should say so, or the light theme is real work. It should not stay ambiguous — the
next person to add a surface cannot tell which rules apply.
It also has live consequences.
DataTab.tsx:46,57setsbg-gray-200 text-gray-600for adeadstatus chip. On a
#0a0a0apage that is a light-grey chip with mid-grey text — not a theme bugwaiting to happen, a legibility bug shipping today. 40 raw Tailwind palette classes are in the tree
(
text-amber-500×8,text-purple-400×7,bg-amber-500×4, plus violet/orange/gray) against astandard that says never hardcode these.
2. Status tokens are named for their colour, not their meaning
The console declares
--color-green,--color-red,--color-yellow,--color-blue. The designsystem declares
--danger,--success,--warning, each with a-softbackground pairing, andsays explicitly: "Use these for validation, alerts, badges, and state — never hardcode
red/green/yellow."
Naming the token after the pigment defeats the purpose one level up.
text-redhas 78 uses carryingat least four unrelated meanings: an error message, a destructive action, the
writescope label ona tool row (
SettingsTab.tsx:854), and an offline/failed state. A reader cannot tell which, and afuture decision to make destructive actions orange-not-red becomes 78 case-by-case judgements.
Rename to intent (
danger·success·warning·info), add the missing-softpairings —several inline
bg-red/10andbg-accent/10opacity hacks exist precisely because the soft variantwas not available.
3. Nothing measures any of this
Whatever is decided above, it needs a check, or it will drift straight back. Cheapest useful version:
store/to the Biome scan (its own PR — expect existing findings, see [quality] Reduce Biome lint debt: any, !important, unused imports, and unsafe patterns #326);*-amber-500,*-gray-200, …) and ontext-[…rem]once [design] The atomic layer under #187: 273 buttons in 14 shapes, 3 card geometries, 17 font sizes #366 has somewhere for those to go;leaving it silently unmet.
Order matters: land #366's primitives first, then lint — a lint with nowhere to put the fix is
just 168 blocked PRs.
Related: #366 (component primitives), #326 (lint scope), #305.