Public: anyone can read this; the crew can change it.
The design lint refuses raw colours, z-index outside the tokens, and fixed boxes outside the overlay host #12019
Check · Review · Land · Live done — Landed and live in production.
The design lint now refuses a raw `z-index`, off-scale spacing, `position: fixed` and a full-viewport box outside `src/web/overlay_host.ts`. Today's violations are named as debt, not fixed. `bun scripts/design-lint.ts` passes on this tree, `bun x tsc --noEmit` is clean, and `test/design-lint.test.ts` passes (48 tests). I did not run the rest of the suite; the forge's check runs it on the push. I d…
shipped production is serving it deploy 1a20738
The design lint now refuses a raw z-index, off-scale spacing, position: fixed and a full-viewport box outside src/web/overlay_host.ts. Today's violations are named as debt, not fixed. bun scripts/design-lint.ts passes on this tree, bun x tsc --noEmit is clean, and test/design-lint.test.ts passes (48 tests). I did not run the rest of the suite; the forge's check runs it on the push. I did not call keep_plan or estimate.
What changed
- Rules: the new module
src/design/layout.tsreads every/* css */template insrc/weband checks four rules:zindex,fixed,viewportandspace.
- zindex flags anything that is not var(--z-…), 0 or auto.
- viewport flags a width or height of 100vw, 100vh or 100dvh. A viewport size used as a bound inside calc(), min() or max() is allowed.
- space flags a px value in padding, margin or gap that is off the scale 2 4 6 8 12 16 24 32 48 64.
- Refusals: each is one line:
file:line rule: declaration → fix. - Debt: it is listed per file and rule under a new
layoutkey indocs/design/baseline.json. It is 198 findings today. A pull can lower a file's count but not raise it, and--updaterewrites it. - Wiring:
scripts/design-lint.tsruns the rules and re-exports them.docs/design/sections/4-space-radius-motion.mdhas a "Layout is tokens" bullet. - Tests:
test/design-lint.test.tsfeeds the lint a sample for each violation and checks each is refused. It also checks the one-line message format, that the overlay host is exempt, that a bound is allowed, and that the real tree is no worse than its debt.
What I chose not to do
- Colour: I added no new colour rule, because the existing
colourratchet already refuses raw colours and its baseline is empty. - Z tokens: the
--z-*tokens do not exist yet, so the rule only accepts the name. The existing raw z-index values stay as named debt until a pull defines the tokens and moves them. - Fixed boxes: I did not move the existing fixed boxes in
components.tsandspace_client.tsinto the overlay host. - Spacing: I did not rewrite the existing off-scale spacing.
To try it: run bun scripts/design-lint.ts, then add z-index: 99 to any /* css */ template to see the one-line refusal.
The preview deploy did not fail, so I changed nothing and pushed nothing. The log ends done: … and the run's own summary is running at …, with the note "no tests declared, none run". The preview answers /healthz with 200.
That note comes from forge.json having no app.test, and it was already so on main. The forge's check runs the tests on each push, and test/design-lint.test.ts passes in that check, 48 tests.
If you want previews to run tests too, "test": "bun test test/design-lint.test.ts" under app would do it. It would also apply to production deploys and every other pull, so I left it for you to decide.
The gate failure is fixed, and I pushed the fix. I didn't run any tests locally. scripts/fast.ts couldn't name tests (it reported the diff as too broad), so I left that to the forge's check on this push.
What changed: I rewrapped the comment block at the top of src/design/layout.ts so no line goes past 100 columns. The two long lines were the fixed and space entries in the rules list. I also dropped the long dashes in that list in favour of colons and shortened one phrase in the viewport entry. Only comments changed, not behaviour.
Checks run: bun scripts/gate.ts and bun x tsc --noEmit both pass. The gate only passed once the rewrap was committed, because it checks the lines the pull adds, not the working tree.
What to look at: the diff is just that comment block in src/design/layout.ts.
The pull itself is unchanged: the design lint refuses raw z-index, off-scale spacing, and fixed or full-viewport boxes outside the overlay host. Today's debt is named in docs/design/baseline.json.
The lint works: bun scripts/design-lint.ts passes on this tree and the design-lint tests pass. Each refusal is one line with the rule and the fix. Debt is named per file under layout in baseline.json, so it only shrinks. This is tooling, so there is no page or terminal surface to try, and the docs bullet sits in the design section, not README. Next two things: (1) define the --z-* tokens and move the 198 named debt entries toward zero, starting with the 7 z-index and 3 fixed entries in components.ts. The z-index rule currently accepts token names that do not exist, so also check that a --z-x name is defined. (2) Add a test for the raw-colour case in the new sample, or say in the test where the existing colour ratchet covers it. The space rule ignores var() fallbacks, which is fine but undocumented.
The design lint now refuses a raw
z-index, off-scale spacing,position: fixedand a full-viewport box outsidesrc/web/overlay_host.ts. Today's violations are named as debt, not fixed.bun scripts/design-lint.tspasses on this tree,bun x tsc --noEmitis clean, andtest/design-lint.test.tspasses (48 tests). I did not run the rest of the suite; the forge's check runs it on the push. I did not callkeep_planorestimate.What changed
src/design/layout.tsreads every/* css */template insrc/weband checks four rules:zindex,fixed,viewportandspace.-
zindexflags anything that is notvar(--z-…),0orauto. -viewportflags a width or height of100vw,100vhor100dvh. A viewport size used as a bound insidecalc(),min()ormax()is allowed. -spaceflags a px value inpadding,marginorgapthat is off the scale2 4 6 8 12 16 24 32 48 64.file:line rule: declaration → fix.layoutkey indocs/design/baseline.json. It is 198 findings today. A pull can lower a file's count but not raise it, and--updaterewrites it.scripts/design-lint.tsruns the rules and re-exports them.docs/design/sections/4-space-radius-motion.mdhas a "Layout is tokens" bullet.test/design-lint.test.tsfeeds the lint a sample for each violation and checks each is refused. It also checks the one-line message format, that the overlay host is exempt, that a bound is allowed, and that the real tree is no worse than its debt.What I chose not to do
colourratchet already refuses raw colours and its baseline is empty.--z-*tokens do not exist yet, so the rule only accepts the name. The existing raw z-index values stay as named debt until a pull defines the tokens and moves them.components.tsandspace_client.tsinto the overlay host.To try it: run
bun scripts/design-lint.ts, then addz-index: 99to any/* css */template to see the one-line refusal.Closes #12017