A public shadcn registry that packages the Crash Override Design System: a brand theme (the oklch light+dark token bridge + fonts) plus 60+ branded components and a handful of composite blocks. Any shadcn app installs branded UI with one command:
npx shadcn add @crashoverride/<name>Tag. Track. Trust. — The Data Plane for Software in the AI Era.
Scope. This design system is for Crash Override's marketing properties (the corporate site
crashoverride.comand the marketing/ops admin) and open-source projects (Chalk, Ocular, con4m, COMPASS). The product Console / Platform is not in scope yet — don't adopt it there until it's been formally onboarded.
Dark-first surfaces · Atomic-lime primary (Fandango in light mode) · JetBrains
Mono (display) / Inter (body) / Geist Mono (mono) · sharp 4px radius. The
theme item ships the oklch light+dark bridge so every component tracks both modes
automatically — no per-component theming required.
1. Point components.json at the @crashoverride namespace. Add the registries
block (or merge it into your existing one):
2. Install the theme first — it is the keystone every component depends on (it carries the brand tokens and fonts):
npx shadcn add @crashoverride/theme3. Add components and blocks as you need them:
npx shadcn add @crashoverride/button
npx shadcn add @crashoverride/card
npx shadcn add @crashoverride/stat-card-row # a composite blockEach item resolves to JSON served from GitHub Pages at
https://crashappsec.github.io/react-registry/r/<name>.json.
The unversioned path (r/{name}.json) always resolves to the current major. If you
need a stable pin -- for example, when you want to control exactly when you absorb
breaking token changes -- use the versioned path instead:
// components.json
{
"registries": {
"@crashoverride": "https://crashappsec.github.io/react-registry/r/v1/{name}.json"
}
}Support policy. The latest two released majors are actively maintained. Once a third major ships, the oldest major enters its end-of-life window; its frozen JSON remains served from GitHub Pages but receives no further updates.
How versioning works. After shadcn build emits public/r/*.json,
scripts/version-registry.mjs copies those files into public/r/v{M}/. The
unversioned path is the latest-major alias by construction. When a breaking change
requires a new major, the prior public/r/v{M}/ snapshot is committed under
frozen/r/v{M}/ and is served verbatim by Pages from that point forward; it is
never regenerated from source.
Composing pinning with automated refresh. The registry-refresh.yml workflow
(described in "Staying current" below) re-runs npx shadcn add --overwrite using
whichever URL is in your components.json:
- Pinned consumers (
r/v1/{name}.json) re-vendor the latest files within their major. They receive non-breaking updates automatically and absorb a major bump only when they deliberately update the version number incomponents.json. - Unpinned consumers (
r/{name}.json) always track the current major. The refresh workflow pulls the newest files on each run, including across major bumps.
shadcn installs are vendored copies: once added, your app holds a snapshot and does not update itself when the registry changes. Add the refresh workflow and your app gets a PR whenever the upstream design system publishes changes:
# .github/workflows/design-refresh.yml
name: Design system refresh
on:
schedule:
- cron: "23 7 * * 1-5" # weekday mornings UTC
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
refresh:
uses: crashappsec/react-registry/.github/workflows/registry-refresh.yml@main
with:
items: theme # space-separated registry items to track
# working-directory: ui # if components.json is not at the repo rootIt re-runs npx shadcn add --overwrite for the listed items; if nothing changed it
exits quietly, otherwise it opens a design-refresh/registry PR with the diff.
Requires the repo/org Actions setting "Allow GitHub Actions to create and approve
pull requests". PRs opened with the default token do not trigger your CI — pass a
PAT/App token as the token secret if you want CI on refresh PRs.
Make sure the refresh target is actually loaded. The
themeitem writesapp/globals.cssandapp/tokens.css(undersrc/if your project has one), and yourcomponents.jsontailwind.cssshould name that sameglobals.css. If your app's entry stylesheet never imports that file, the job stays green forever while changing nothing: every run succeeds, and the first real refresh PR updates a file nobody loads. After installing the theme, confirm that path is reachable from the app's entry (imported by the root layout or main stylesheet).If you also keep a hand-maintained copy of the theme (a stylesheet pasted from this registry and loaded instead of the refresh target), the two will silently diverge: refreshes update the target, not the copy. Import the refresh target directly, or have the copy
@importit, so exactly one file changes.
- theme — brand token bridge (oklch light+dark) + JetBrains Mono / Inter / Geist Mono.
- gibson-theme — INTERNAL ONLY. Dark-led theme for the Gibson admin portal
(gibson-hackers.com) and other internal apps: near-black indigo surfaces, phosphor-lime
primary. Never use it on crashoverride.com or any public/customer-facing property —
public properties use
theme. Brand spec:docs/gibson-home-spec.md. - Components (64) — real shadcn/Radix components themed for the brand: layout
primitives, forms, overlays, navigation, data display, plus brand-specific extras
(
status-badge,tag,code-panel,typography,chart,logo,tagline). - Blocks (3) — composites built from the components above:
stat-card-row,service-detail-header,service-table. - Hooks —
use-mobile.
The authoritative list of items lives in registry.json, and the
generated JSON in public/r/ after a build.
The source of truth for brand tokens and design guidelines is the crashappsec/brand-visual repository, published to https://crashappsec.github.io/brand-visual/.
This repo (react-registry) is the React implementation layer: it consumes
@crashoverride/brand-tokens (published from brand-visual) to materialize
src/registry/crashoverride/theme/tokens.css, and packages the result as a shadcn
registry (components + the theme item). Design decisions, color values, typography
specs, and brand assets all originate in brand-visual; changes to the token canon
flow into this repo through the automated staleness-gate CI and registry-refresh workflow.
The registry and its brand guidelines are published to GitHub Pages:
- Landing page: https://crashappsec.github.io/react-registry/
- Registry items:
https://crashappsec.github.io/react-registry/r/<name>.json - Brand guidelines:
https://crashappsec.github.io/react-registry/guidelines/
npm install
npm run dev # specimen app — visual QA of every registry item
npm run registry:build # compile registry.json -> public/r/*.json (shadcn build)
npm run test # vitest (smoke tests + build validation)
npm run test:storybook # axe a11y smoke-check (needs a served storybook-static)
npm run build # type-check + vite production build
npm run lint # eslintThe specimen app (src/specimen/) renders every item in both light and dark for
visual review; it is the human gate. src/test/registry-build.test.ts is the machine
gate that validates the build output.
See CONTRIBUTING.md for the component recipe (how to add a new item).
The components are built on Radix primitives, which carry the interaction
accessibility (keyboard, focus management, ARIA roles and states) for us. On top of
that, an axe smoke-check keeps the rendered, themed output honest: the Storybook
a11y addon runs in test: error mode, so every story is scanned by axe and any
violation (missing accessible name, bad role nesting, insufficient contrast) fails the
story test. The a11y CI job builds Storybook, serves it, and runs the test runner on
every push and PR.
Run it locally:
npm run build-storybook
npx http-server storybook-static -p 6006 -s &
npx wait-on http://127.0.0.1:6006
npm run test:storybook -- --url http://127.0.0.1:6006Known contrast debt. A handful of brand-palette tones (Fandango cobalt and Jazzberry magenta text, and
muted-foregroundmicro-text) sit just under the 4.5:1 AA ratio at small sizes on the dark surface. Those values come from the brand-visual token canon (materialized intotheme/tokens.cssand gated by CI here), so the fix belongs upstream. Those few stories scope only thecolor-contrastrule off, each with a justifying comment; every other axe rule still runs on them.
A token change should not silently restyle the system, so a Playwright screenshot
suite (src/test/visual/) snapshots a representative slice of the Storybook build (the
Foundations pages + one story per component/block family) and compares it to committed
baselines. The visual-regression workflow runs only when a token-affecting path
changes (package.json, package-lock.json, src/registry/crashoverride/theme/**),
uploads the diff images as an artifact on failure, and an intentional change is accepted
by refreshing the baselines.
npm run build-storybook
npx http-server storybook-static -p 6006 -s &
npx wait-on http://127.0.0.1:6006
npm run test:visual # verify against committed baselines
npm run test:visual -- --update-snapshots # accept an intentional visual changeBaselines are per-OS. chromium renders fonts differently on macOS vs Linux, so snapshots carry a
-{platform}suffix. The committed-darwinset is the local reference; the-linuxset used by CI is generated in CI. Run thevisual-regressionworkflow'sworkflow_dispatch(Actions tab) to seed or refresh it on a branch. This avoids the flaky-baseline trap of committing macOS pixels that CI can never match.
The implementation plan and design spec live in the COMPASS repository under
docs/superpowers/plans/2026-06-08-crash-override-react-design-system.md.
MIT © Crash Override, Inc. Free to use, copy, modify, and vendor into your
own applications (including via shadcn add). The Crash Override name, logo, and visual
identity remain trademarks of Crash Override, Inc.