A design is a plugin like any other. It ships a light and a dark palette, its
own faces, its own copy of all 31 kit components, signature styling for the
landing page, and a DESIGN.md written for an agent to read. Pick a different
one and the whole repo changes look (landing page, app shell, admin panel,
blog, auth screens, AI chat) without touching a single feature template.
That only works because of one rule, and the rule is enforced, not requested.
There are 31 today, from quiet greys to loud editorial. Compare any two at
/designs/compare.
What a design ships
registry/designs/<id>/
manifest.yaml label, theme (light or dark), tags, the 33 overrides
design.yaml the published token set
DESIGN.md the system, described for an agent
preview/ captured from a real repo, never drawn by hand
template/
slots/tokens.css both palettes, the type and radius scales, landing styles
src/app/fonts.ts the faces, loaded with next/font
src/lib/design.ts the design's name and the theme it opens in
src/components/ui/ all 31 kit files, restyled
agent/
rules/tokens-only.md tokens only, never a raw colour
skills/new-component.md
agents/designer.md
solutions/*.md
design.yaml matches the DesignTokens type in packages/core/src/types.ts:
colours, a type scale (size, weight, line height and letter spacing per step),
spacing, radius, shadows and fonts. It mirrors tokens.css value for value,
and a test fails the moment the two drift.
DESIGN.md is the prose half: the feel, the palette table, the type scale, how
each component looks, the do's and don'ts. It is copied into the generated repo,
and the designer agent reads it before it decides anything.
The whole kit, restyled
The stack ships the kit every repo gets: shadcn/ui's API on Radix, merged with
cn. Dialog, alert dialog, dropdown menu, select, tabs, table, sheet, popover,
tooltip, toast, form fields and more.
A design ships its own copy of every one of those files. It changes every class
string and none of the API: the same exports, props, defaults and data-slot
values, so a page written against the kit works in every design. A test in the
generator checks every design's kit against the stack's, file by file.
The landing page
Every repo gets the same landing page, so designs compare fairly: a hero with a
drawn product preview, a logo strip, features, three showcase rows, how it
works, numbers and quotes, pricing (with a payments battery), FAQ and a closing
call to action. All the copy lives in src/lib/site.ts.
Its sections carry data-slot hooks (hero-backdrop, section-eyebrow,
cta-band and 36 more), and a design styles them in tokens.css. That is how
one page gets a dotted grid and a lime caret in one design, dashed rules and a
periwinkle mark in another, and a newspaper masthead in a third.
Light and dark
Every design ships both palettes in tokens.css, with the same token names:
:rootholds the light palette.- The dark palette sits under
prefers-color-scheme: darkand under adarkclass, with identical values in both. @theme inlinemaps every Tailwind utility onto those variables, so one class follows the mode.
A design is drawn in one of the two first, and the generated app opens in that
one (DEFAULT_THEME in src/lib/design.ts). The header's theme menu still
offers light, dark and system. A test holds every design to WCAG AA contrast in
both modes.
Previews you can trust
The screens on /designs are not drawings. They are captured from a
repo the generator really produced, running next dev, with the design's real
fonts and CSS: the landing page, sign-in and the dashboard, in light and dark.
What you see there is what bun run dev shows on your machine.
The token contract
Every template in the registry uses tokens only. No raw colour utility classes. No hex values outside a design's own token files.
bun run validate in the generator enforces it. Its template lint fails on:
| Issue | What it catches |
|---|---|
raw-color-class | a palette utility like bg-blue-500 in a template |
raw-color-hex | a hex value outside a design's own token files |
raw-color-neutral | a raw neutral where a token exists |
hardcoded-pm | a literal runner (npm run, npx, bunx, pnpm dlx, pnpm exec) instead of a {{pm}}-style token |
This lint guards the registry. It does not run in your generated repo's build.
There, the tokens-only rule tells your agent the same thing.
Three things follow from the rule:
- Designs swap cleanly. The checkout page, the admin sidebar and the blog index are written once and take whichever design you picked.
- Agents have one place to look. "Which grey is the muted one?" has one
answer, in
globals.css, andDESIGN.mdexplains it. - The diff stays readable. A visual change shows up as a token change, not as forty component files each nudged by hand.
The designer agent
designer is the only agent allowed to add a new visual pattern or a new token.
Every other agent builds from what exists.
A repo where any agent can add a component drifts within a week: three button variants, two card paddings, a one-off shadow. Send visual decisions through one agent with one document, and drift has to be argued for.
Use it for: a component the kit doesn't have yet, a screen that needs a layout the kit doesn't cover, a real gap in the token set.
Don't use it for: building a page from components that already exist. Any
agent can do that, and the tokens-only rule keeps it honest.
Adding a design
A new design has to pass the same checks as the ones that ship: both palettes
at WCAG AA, all 31 kit files against the stack's surface, validate, combos,
a real generate, install, typecheck, lint, test and build, and a captured
preview. Nirali reviews design pull requests.
See contributing.