Skip to content

feat(design-tokens): complete the CSS-var surface so the website can leave inline styles - #845

Merged
blove merged 17 commits into
mainfrom
blove/docs-visual-polish-832da3
Aug 29, 2026
Merged

feat(design-tokens): complete the CSS-var surface so the website can leave inline styles#845
blove merged 17 commits into
mainfrom
blove/docs-visual-polish-832da3

Conversation

@blove

@blove blove commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Why

A visual review of the docs site found real defects — both sticky rails are dead site-wide, .docs-table-scroll never scrolls so a props table renders agent as ag/en/t on a phone, and no scroll-margin exists so every deep link lands behind the 81px fixed nav. Roughly half the fixes cannot be written, because the components style themselves with inline style={{}} objects, and inline styles cannot express :focus-visible, :hover, :last-child, or a media query.

That makes a substrate migration the prerequisite. It splits into three projects; this is project 1 of 3, and it ships no user-visible feature. It exists so project 2 (802 inline style objects across 90 files) has somewhere to migrate to.

Findings audit: docs/superpowers/audits/2026-08-29-docs-visual-review-findings.md
Design: docs/superpowers/specs/2026-08-29-design-token-css-var-completion-design.md
Plan: docs/superpowers/plans/2026-08-29-design-token-css-var-completion.md

The gap this closes

The website reads tokens from JS (tokens.colors.accent) and from CSS (var(--color-accent)). Both derive from light.ts, so where a var exists the values are identical by construction. But generate-theme-css.ts only ever emitted colors, fonts, radii and shadows — so 217 of 1,188 token references had no CSS counterpart at all.

token group refs had a var before
colors.* / surfaces.* 687
font families 86
radius.* / shadows.* 72
composite .family 122 ✅ (value is already var(--font-garamond))
type scale .size/.line/.weight/.letterSpacing 210
space.* 7

What changed

  • Type scale → 7 Tailwind v4 composite --text-* steps carrying --line-height / --font-weight / --letter-spacing sub-keys, so one text-h1 utility replaces four inline properties in project 2.
  • Space scale--spacing-section-y, --spacing-section-y-tight, --spacing-container-x, --container-page.
  • tokens.css was hand-written, orphaned, and drifted (--ds-text-secondary: #555770 vs the live rgb(70,70,70)). Now generated from light.ts. Gains 5 names, loses none.
  • global.css — 18 hardcoded literals resolved in three separately-committed categories, so the one visible change is isolated and revertable on its own.
  • 4 new guard specs, 86 tests green.

The one visible change

Docs table header text and body text move off a dead colour. #555770 came from the old --ds-* surface and no longer matches any token.

before after
.docs-prose th rgb(85, 87, 112) rgb(115, 115, 115)
.docs-prose td rgb(85, 87, 112) rgb(70, 70, 70)

Both measured in a browser. th and td deliberately take different tokens: td is body content and matches --tw-prose-body, which MdxRenderer already sets to colors.textSecondary; th is an uppercase mono label and belongs with the other muted labels.

Two other uses of #555770 were already var(--color-text-muted, #555770) — the var is defined, so those fallbacks were dead text. Confirmed: li::marker measured rgb(115,115,115) both before and after. Nothing else on any page changes.

Verification

Every guard was mutation-tested — broken deliberately, confirmed red, reverted:

guard mutation result
parity radius.md 10px→11px RED, 1 of 63
parity space.sectionY clamp max RED, named case
parity (light.*) added light.probeOnly RED, named light.probeOnly
--ds-* contract renamed --ds-canvas RED, named missing
--ds-* contract dropped --ds-render-green RED, named missing
tokens.css drift hand-edited a radius RED, theme.css case still green
cssVars agreement --ds-accent#000000 RED, named the mismatch

Browser-measured rather than assumed: text-h236px with 40.32px line-height (= 36 × 1.12, proving the --text-*--line-height sub-key is honoured), p-section-y64px, max-w-page1200px.

Also: nx test design-tokens green (86), nx build website --configuration=production succeeds, 0 lint errors in both projects.

The website vitest suite reports 5 failed | 341 passed. Those 5 are pre-existing — content assertions in PostCard, Differentiator, and thanks/page that drifted red because there is no nx test website target so they never ran in CI. Same 5 at the baseline commit; unrelated to this work and out of scope here.

Corrections made during the work

Recorded because each was a wrong belief caught by measurement:

  1. --container-page generates max-w-page, not max-w-container-page — Tailwind strips the namespace prefix. The spec, the plan, and a code comment all had it wrong.
  2. .shiki and [data-rehype-pretty-code-title] are dead CSS. rehype-pretty-code runs with keepBackground: true, so it writes the theme background inline on the <pre> and never emits a .shiki class; no code fence uses title=. Zero matches across docs and blog. Documented as findings §9 — deleting them is cleanup, not this project.
  3. A contrast defect, caught in final review: adopting --color-text-muted onto the dark #1a1b26 code-title bar drops contrast 5.34:1 → 3.60:1, below AA. Dead CSS today, but it contradicted the change's own rationale — that bar is coupled to the tokyo-night theme. Its foreground now lives in --docs-code-title-fg with the other theme-coupled constants.

Deliberately not done

No component style={{}} is touched (project 2). No fix from the findings audit lands (project 3) — the sticky rails, the crushed tables, and the anchors behind the nav all stay broken. No dark theme. The cockpit apps are not wired to import tokens.css; they still render on their var(--ds-*, fallback) fallbacks, and changing that would flip nine example apps from dark fallbacks to light token values — its own ticket.

🤖 Generated with Claude Code

blove and others added 17 commits August 29, 2026 11:48
…dings

A visual review of the docs site found a set of measured defects: both sticky
rails are dead site-wide (body overflow-x makes <body> a scroll container),
.docs-table-scroll never scrolls because the table is width:100% with no
min-width, and no scroll-margin exists so every deep link lands behind the
81px fixed nav.

Roughly half the fixes are inexpressible in the inline style={{}} objects the
components use, so the work decomposes into three projects: token CSS-var
completion, substrate migration, then the polish arc.

Adds the findings audit (evidence log, reproducible) and the spec for project
one — emitting the type and space scales that generate-theme-css.ts never
learned about, resolving 18 hardcoded literals in global.css, and adding a
machine-checked parity test so the migration's core premise is verified rather
than assumed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ten TDD tasks: parity spec first (red), then emit the type scale as Tailwind v4
composite --text-* tokens and the space scale, bring the orphaned tokens.css
under the generator, ship it, and resolve global.css's 18 literals in three
separately-committed categories so the one visible change is isolated.

Validated the parity spec's logic against the real tokens in the real vitest
runner before writing it down: 104 token leaves, 34 vars parsed today, and six
spot-checked paths are value-identical, which is the premise the substrate
migration rests on.

Also corrects the spec: two of the five stale-literal uses are already
var(--color-text-muted, #555770) fallbacks that render the token today, so the
visible change is bounded to table header text, table body text, and code-block
titles — not list markers and figure captions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The literals #555770 and #8b8fa3 came from the old --ds-* surface and no
longer match any token. Visible change, bounded to table header text, table
body text, and code-block titles.
…findings

Three corrections, all measured in a browser rather than reasoned about:

1. Tailwind strips the namespace prefix, so `--container-page` generates
   `max-w-page`, not `max-w-container-page`. The spec, the plan, and a
   committed code comment all had it wrong. Verified: `max-w-page` computes to
   1200px, `p-section-y` to 64px, and `text-h2` to 36px/40.32px — which also
   confirms the `--text-*--line-height` sub-key is honoured.

2. `.shiki` and `[data-rehype-pretty-code-title]` are dead CSS. rehype-pretty-code
   runs with keepBackground:true, so it writes the theme background inline on
   the <pre> and never emits a .shiki class; no code fence uses `title=`. Zero
   matches on docs and blog. That narrows the visible surface of the token
   adoption to table header and table body text only. Recorded as findings §9;
   deleting dead rules is cleanup, not this project.

3. color-mix resolves and Lightning CSS emits a hex fallback plus an @supports
   upgrade, so the open question in the spec resolves in its favour.

Also documents the website suite's 5 pre-existing failures as the baseline, so
Task 10 does not read them as a regression.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…nstants

var(--color-text-muted) is a light-surface role token; on the dark #1a1b26
title bar it drops to 3.6:1, below AA. Move it to --docs-code-title-fg
alongside the other tokyo-night-coupled values.
… to the contract

Referenced 13x across cockpit/render. It was absent from the list because the
list was derived against the pre-generator tokens.css, which did not define it.
Two hand-maintained emitters of the same --ds-* namespace, from the same
sources, with nothing enforcing agreement. They match today; this keeps them
matching.
@vercel

vercel Bot commented Aug 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
threadplane Ready Ready Preview Aug 29, 2026 6:52pm

Request Review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated approval: this PR received an intelligent (AI) code review. See the review comments on this PR.

@github-actions

github-actions Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Claude finished @blove's task in 0s —— View job


I'll analyze this and get back to you.

@blove
blove enabled auto-merge (squash) August 29, 2026 18:50
@blove
blove merged commit 6c0685b into main Aug 29, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant