Skip to content

Phase 2: consume @crashoverride/brand-tokens; versioned registry; a11y + visual CI - #5

Merged
curphey merged 12 commits into
mainfrom
feat/brand-tokens-consumption
Jun 11, 2026
Merged

curphey merged 12 commits into
mainfrom
feat/brand-tokens-consumption

Conversation

@curphey

@curphey curphey commented Jun 11, 2026 •

Copy link
Copy Markdown
Collaborator

Phase 2: consume @crashoverride/brand-tokens — Readiness Report

Updated 2026-06-11. Supersedes the original description; nothing material removed, everything verified against the live branch (12 commits, HEAD 9ecaa8c).

Verdict: READY TO MERGE

Check Status
build (tsc + vite + registry build + theme staleness gate) ✅ pass
a11y (axe smoke-check, 308 story tests, a11y.test: error) ✅ pass
verify (visual regression vs 73 seeded Linux baselines) ✅ pass
vitest ✅ 198/198
Registry output semantic diff vs pre-migration ✅ 1 intentional change out of 290 token declarations

What this PR does

react-design-system stops owning the token canon and becomes the React implementation that consumes it:

  • Registry theme materialized from @crashoverride/brand-tokens (exact-pinned github:crashappsec/brand-visual#v1.0.1) by scripts/build-theme-tokens.mjs; CI fails if the committed artifact is stale.
  • Local canon deleted (public/tokens/, public/brand-assets/); 20 guideline pages are now redirect stubs to brand-visual Pages for one release cycle (Remove guideline redirect stubs after one release cycle #4 tracks removal).
  • Versioned registry: r/v1/<name>.json served alongside r/<name>.json (latest alias); frozen-major path wired for future breaking changes; two-major support policy in the README.
  • New CI guardrails: axe a11y smoke-check (3 real component fixes: slider, combobox, scroll-area) and Playwright visual regression on token-bump paths.
  • Storybook foundation pages re-sourced from brand-visual with canonical-source banners; README scope note.

The only value change consumers will see

--border-default2 (light mode): #9D9D9D → #8E8E8E — the WCAG 2.2 SC 1.4.11 fix (2.71:1 → 3.28:1) adopted from the canon. Fonts additionally become self-hosted-first (brand-visual Pages) with the gstatic fallback retained. Everything else in the registry output is value-identical (verified by token-level diff of all 290 declarations).

Exactly what is left

To complete this rollout, in order:

  1. Merge this PR (admin-merge or review).
  2. Pages auto-redeploys (~2-3 min): r/v1/ goes live, redirect stubs go live, registry serves the materialized theme.
  3. Post-merge smoke (~10 min, will be run and posted here): scratch app npx shadcn add @crashoverride/theme against both the latest and r/v1/ URLs; confirm curl r/theme.json == r/v1/theme.json; spot-check one redirect stub.
  4. Consumer refresh PRs land: compass and digital-marketing run their scheduled design-refresh callers weekday mornings (07:23 UTC), or trigger immediately via workflow_dispatch. Owners review a one-token visual diff and merge.

Tracked follow-ups (none block the merge):

  • Remove guideline redirect stubs after one release cycle #4: remove the guideline redirect stubs after one release cycle.
  • brand-visual#5: 13 genuine brand-palette contrast failures (Fandango/Jazzberry/muted-foreground as text fail WCAG AA). Scoped off per-story here with justifying comments; resolution belongs in the brand-visual canon (Claude Design dark-theme audit). When fixed upstream, remove the per-story scope-offs in .storybook/stories.
  • brand-visual#1: SVG logo lockups (v1.1.0), optional npm publish channel.
  • Token-bump automation: git-tag dependencies are not tracked by Dependabot. Token bumps are deliberate one-line PRs (#v1.0.1 → #v1.0.2) until either Renovate is adopted or the npm channel is enabled. The visual-regression job triggers on exactly those PRs.

What users need to do

React apps consuming the shadcn registry (compass, digital-marketing, OSS sites):

  • Nothing breaks on merge. Vendored copies are snapshots; existing apps keep working untouched.
  • To adopt the update: merge the automatic design-refresh PR when it arrives, or run npx shadcn add @crashoverride/theme --overwrite manually. The diff you will review: the one border token, the font src changes, and a provenance header.
  • Optional version pin (recommended for production apps), in components.json:
{ "registries": { "@crashoverride": "https://crashappsec.github.io/react-design-system/r/v1/{name}.json" } }

Pinned consumers re-vendor within their major; unpinned consumers track latest.

Apps that want raw tokens without React/shadcn (new capability):

// package.json
{ "dependencies": { "@crashoverride/brand-tokens": "github:crashappsec/brand-visual#v1.0.1" } }
@import "@crashoverride/brand-tokens/tokens/colors.css";
@import "@crashoverride/brand-tokens/themes/corp/theme.css";

Anyone with links to the old guideline pages (bookmarks, Slack history, docs): old URLs redirect automatically for one release cycle; update links to https://crashappsec.github.io/brand-visual/guidelines/<page>.html.

Contributors to this repo:

  • Do not edit src/registry/crashoverride/theme/tokens.css — it is generated and CI-gated. Token changes go to brand-visual (marketing review per CODEOWNERS), then arrive here as a dependency bump.
  • Visual baseline refresh: run the Visual regression workflow's workflow_dispatch update mode; it uploads regenerated baselines as an artifact (the org's verified-signature rule blocks bot pushes), which a maintainer commits with a signed push. Documented in the README.

Rollback

git revert of the merge restores everything: the registry rebuilds from committed sources on the next Pages deploy, the deleted canon is recoverable from history and independently from brand-visual, and the redirect stubs make rollback invisible to guideline readers in both directions.

Reconciliation note (for the record)

This migration surfaced and fixed a real two-copy drift: commit e4e883d had fixed the tagline traffic-light colors only in the registry copy, never in public/tokens/; brand-visual inherited the stale values. Fixed upstream as brand-visual v1.0.1, consumed here. After this merge, that class of bug is structurally impossible: one canon, one materialization path, staleness-gated.

🤖 Generated with Claude Code

curphey and others added 12 commits June 11, 2026 13:25
…ens (adopts light-border a11y fix)

The design system no longer owns a token canon. brand-visual (published as
@crashoverride/brand-tokens, pinned at v1.0.1 by git tag) is the single
source of truth. scripts/build-theme-tokens.mjs concatenates the package
token layers in the registry order (colors, spacing, typography, semantic,
base) into src/registry/crashoverride/theme/tokens.css with a provenance
header. Output is deterministic (LF, no timestamps) so CI can gate staleness.

Wiring:
- package.json: add "theme:build"; "registry:build" now runs theme:build
  before shadcn build.

Materialized tokens.css differs from the previously committed copy on exactly
two things, both expected:
1. The provenance header line.
2. Light-mode --border-default2: #9D9D9D -> #8E8E8E. This is the INTENTIONAL
   adoption of the brand-visual WCAG SC 1.4.11 accessibility fix. It is the
   only value change; verified by a comment-and-whitespace-stripped diff that
   isolates this single line.
The tagline comment punctuation also changes (em-dash to colon) to match the
package source, but the CSS values are byte-identical there; no functional
change.

globals.css @font-face is now self-hosted-first to match brand-visual: the
primary src is the vendored OFL woff2 from the brand-visual Pages site, with
the gstatic latin-subset woff2 kept as a same-declaration fallback so
consumers who cannot reach Pages are not broken.

Verified: npm run registry:build green; generated public/r/theme.json carries
the border fix and dual font URLs; npx vitest run green (198 passed).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
After registry:build (which runs theme:build), fail CI if the materialized
src/registry/crashoverride/theme/tokens.css is not byte-identical to what the
build produces. This guarantees the checked-in theme canon always reflects the
pinned @crashoverride/brand-tokens version and cannot drift via hand edits.

Verified locally: a one-char edit makes git diff --exit-code return non-zero;
reverting and re-running theme:build returns to zero (deterministic output).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The materialized registry theme (src/registry/crashoverride/theme) is now the
single in-repo consumer of brand tokens; public/tokens/ and public/brand-assets/
were stale local copies. Delete them and re-source the static preview pages and
the Brand Assets MDX from the live brand-visual Pages deploy.

- public/styles.css: re-point the 7 token @imports at the brand-visual Pages
  tokens (these static pages cannot resolve a node_modules path).
- src/docs/BrandAssets.mdx: point img src at brand-visual Pages assets as a
  minimal fix so Storybook still builds; left a TODO(Task 5) to re-source properly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace all 20 public/guidelines/*.html pages with minimal meta-refresh
redirect stubs pointing at the equivalent brand-visual Pages URLs
(https://crashappsec.github.io/brand-visual/guidelines/<page>.html).
Body link provided for clients that block meta-refresh.
Cleanup tracked in #4.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…scope note

- Colors.mdx, Typography.mdx: add canonical-source banner linking brand-visual
  guidelines (color-palette.html, type-fonts.html); existing swatch rendering
  unchanged.
- BrandAssets.mdx: remove TODO(Task 5) comment; keep img src on brand-visual
  Pages assets (the correct re-source per spec); add canonical-source banner
  linking brand-visual guidelines/brand-logos.html.
- README.md: add "Token and guideline canon" section clarifying that
  crashappsec/brand-visual is the source of truth and this repo is the React
  implementation layer consuming @crashoverride/brand-tokens.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…0 feedback)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Flip the a11y addon to `test: error` so axe violations fail story tests, add
the Storybook test runner, and run it against the static build in a new CI
`a11y` job (build-storybook, serve, test-storybook).

Triaged the real tree (308 story tests). Fixed 42 name-gap violations by
modelling accessible usage: aria-label on bare demo controls (switch, checkbox,
progress, slider, input/textarea/input-group/input-otp) and component-level
forwarding where the name must land on a specific element (slider thumb,
combobox role=combobox trigger, scroll-area viewport made keyboard-focusable).
The remaining 13 color-contrast findings are brand-palette tones (Fandango /
Jazzberry / muted-foreground micro-text) materialized from the brand-tokens
canon and CI-gated here, plus one upstream cmdk separator-in-listbox; those
stories scope OFF only the offending rule with a justifying comment (no blanket
disables) and are tracked as contrast debt in the README. Verified green
(308/308) and confirmed the check catches a deliberately bad contrast color.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Snapshot a representative slice of the Storybook static build (the Foundations
pages + one story per component/block family, derived from index.json so it
never drifts) and diff it against committed baselines, so a token change cannot
silently restyle the system.

The new visual-regression workflow triggers ONLY on token-affecting paths
(package.json, package-lock.json, src/registry/crashoverride/theme/**), builds
and serves Storybook, runs the suite, and uploads diff images as an artifact on
failure. The pixel ceiling (maxDiffPixelRatio 0.005) was calibrated against a
real change: flipping the primary accent diffed affected stories by ~1% of
pixels, well above the threshold, while sub-pixel antialiasing stays under it.

Baselines carry a -{platform} suffix because chromium renders fonts differently
on macOS vs Linux. The committed -darwin set is the local reference; the -linux
set CI compares against is generated IN CI via the workflow's workflow_dispatch
update mode (which validates the target ref and commits the refreshed baselines)
-- this avoids committing macOS pixels that CI could never match. Verified: a
no-op re-run passes against the committed baselines, and a deliberate accent
change produces diffs (then reverted).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
git diff --quiet ignores untracked files, so the freshly generated
-linux baselines were never committed. Stage first, then check the
index with git diff --cached --quiet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…fact

The org's verified-signature rule blocks the bot from auto-committing
Linux baselines (GH013). Two fixes: (1) the verify guard skips when no
-linux baseline is committed yet instead of hard-failing (a snapshot
guard with no baseline guards nothing), activating once seeded; (2) the
update-baselines job uploads the generated baselines as an artifact so a
maintainer can commit them with a signed push.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Generated by the update-baselines CI run (artifact), committed here via a
signed push since the org verified-signature rule blocks the bot. This
activates the visual-regression guard: token-bump PRs now diff against
these 73 Linux baselines.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@curphey
curphey merged commit ebb9383 into main Jun 11, 2026
4 checks passed
@curphey
curphey deleted the feat/brand-tokens-consumption branch June 11, 2026 13:24
@curphey

curphey commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator Author

Post-merge smoke: ALL PASS ✅

  1. Registry endpoints: r/theme.json and r/v1/theme.json deployed and byte-identical.
  2. Migration markers in deployed output: provenance header (@crashoverride/brand-tokens@1.0.1) ✅ · border fix #8E8E8E ✅ · self-hosted brand-visual font srcs ✅ · traffic-light tagline ✅.
  3. Redirect stubs live: old guideline URLs 200 and redirect to brand-visual Pages (spot-checked color-palette.html).
  4. Consumer install, latest: npx shadcn add @crashoverride/theme in a scratch project vendors app/tokens.css + app/globals.css with the migrated content.
  5. Consumer install, pinned r/v1/: vendored output identical to latest.

Harness note for anyone repeating this: the scratch project needs a tsconfig.json or the shadcn CLI aborts before fetching; that's a CLI requirement, not a registry issue.

Consumer refresh workflows (compass, digital-marketing) being triggered now; they'll open refresh PRs offering this update.

🤖 Generated with Claude Code

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