Skip to content

Designs and the token contract

What a design ships, why every design restyles the whole kit, how the previews are made, and how to use the designer agent.

5 min read

PagesDesigns and the token contract

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:

  • :root holds the light palette.
  • The dark palette sits under prefers-color-scheme: dark and under a dark class, with identical values in both.
  • @theme inline maps 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:

IssueWhat it catches
raw-color-classa palette utility like bg-blue-500 in a template
raw-color-hexa hex value outside a design's own token files
raw-color-neutrala raw neutral where a token exists
hardcoded-pma 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:

  1. Designs swap cleanly. The checkout page, the admin sidebar and the blog index are written once and take whichever design you picked.
  2. Agents have one place to look. "Which grey is the muted one?" has one answer, in globals.css, and DESIGN.md explains it.
  3. 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.