Skip to content

Getting started

Customize the system

Replace the visual identity without rebuilding the documentation. Tokens, presets and product identity are configurable; the existing page shell, component anatomy, usage guidance, API tables and accessibility notes stay intact.

Try the theme contractPermalink to this section

These controls change the real site, not an isolated mockup. Your preference is saved locally and restored before the next page paints.

Colour mode
Design preset

Live component contract

Colour, radius, sizing and elevation all come from the active tokens.

Preview

What changes, what staysPermalink to this section

Visual identity and documentation structure are deliberately separate concerns.

AreaSourceEffect
Brand and status coloursrc/tokens/primitives.tsRecolours every semantic and component consumer
Light and dark rolessrc/tokens/semantic.tsChanges surfaces, text, borders and states per mode
Control shape and sizingsrc/tokens/components.tsReshapes buttons, fields, dialogs, cards and menus
Typography scalesrc/tokens/typography.tsUpdates every .type-* utility from one definition
Theme presetssrc/tokens/presets.tsAdds a selectable brand and shape combination
Name, package and repositorysrc/config/site.tsRenames product identity across shared navigation and metadata
Docs information architecturesrc/app/docs + src/components/docsProtected: keep the existing page structure and content model

1 · Replace the visual foundationPermalink to this section

Start at the primitive layer for a real rebrand. Semantic names and component contracts should usually survive unchanged.

src/tokens/primitives.ts
export const primitiveGroups = [
  {
    id: "brand",
    tokens: {
      "brand-50":  { value: "#f5f8ff", description: "Faint brand wash." },
      "brand-600": { value: "#2457d6", description: "Primary brand action." },
      "brand-700": { value: "#1946b8", description: "Primary hover." },
      // Keep every step referenced by semantic.ts.
    },
  },
];

2 · Reshape componentsPermalink to this section

The control contract keeps equivalent sizes aligned across Button, Icon Button, Input, Textarea and Select. Component-specific radius and padding tokens allow intentional exceptions.

src/tokens/components.ts
"control-radius": {
  value: "radius-full",
  category: "radius",
  description: "Shared button and field corner.",
},
"control-height-md": {
  value: "44px",
  description: "Default control height.",
},

3 · Add your own presetPermalink to this section

A preset is a named set of token overrides. It can change only colour, only shape, or both, and is automatically exposed by both theme controls.

src/tokens/presets.ts
{
  id: "my-brand",
  label: "My Brand",
  description: "The product identity used by our application.",
  overrides: {
    "brand-600": "#2457d6",
    "brand-700": "#1946b8",
    "control-radius": "radius-sm",
    "card-radius": "radius-md",
  },
}

A full-foundation preset, worked end to end

The resendpreset (“Resend template”) is a complete reference: a dark-native translation that overrides the neutral and brand ramps, surface and border roles, radius, weight, motion and the signature hairline-ring elevation — while retargeting only the primary button to the inverted surface so its call to action stays white-on-black without touching the accent ramp. It authors both light and dark from one set of ramps, exactly as the rules below require. Read it in src/tokens/presets.ts before writing your own.

4 · Apply a preset to one subtreePermalink to this section

A preset does not have to own the whole page. Any element carrying data-theme and data-preset becomes a scope, and every component below it resolves that preset instead of the page's.

Page presetwhite-black

Deploy preview

Colour, typeface, radius, density, elevation and timing all resolve from whichever preset this subtree sits in.

data-preset="replit"replit

Deploy preview

Colour, typeface, radius, density, elevation and timing all resolve from whichever preset this subtree sits in.

page.tsx
// Both attributes are required: the generated rules key on the pair,
// so a scope that names only data-preset will not match. Pass the page's
// resolved mode so the subtree still follows light and dark.
<div data-theme={resolvedTheme} data-preset="replit">
  <Card>
    <Button>Deploy</Button>
  </Card>
</div>

No second runtime

Scoping is entirely CSS custom-property inheritance. There is no nested provider, no context and no per-preset branch inside any component — a component reads var(--button-radius) and gets whichever value its ancestor scope resolved.

5 · Change product identity and layoutPermalink to this section

Text identity is centralized. Layout dimensions are semantic tokens, so changing the shell does not require editing the docs page templates.

src/config/site.ts
export const siteConfig = {
  name: "Your Product",
  fullName: "Your Product Design System",
  wordmarkSuffix: "Design System",
  package: "@your-org/ui",
  repository: { label: "your-org/ui", url: "https://github.com/…" },
};

Keep the documentation contract

Do not replace the docs layout, the four component-page tabs, anatomy, usage guidance, API tables or accessibility content during a visual customization. The detailed AI checklist lives in CUSTOMIZATION.md.

VerificationPermalink to this section

A customization is complete only when the generated contract, documentation and both colour modes agree.

  1. 1Run npm run lint and npm run build. Registry validation fails loudly on missing aliases, duplicate names and cycles.
  2. 2Review the homepage plus Button, Input, Dialog and token explorer pages in light and dark mode.
  3. 3Switch every preset and check focus, hover, invalid and disabled states — not only the resting component.
  4. 4Confirm the existing docs routes and page sections are unchanged unless the content change was explicitly requested.