Theming
One token file drives everything. Standard shadcn variables stay untouched - ai2 adds tone channels, surfaces and motion on top.
How it works
In this repository, tokens/tokens.json is the single source of truth. A build script generates the CSS, and the registry publishes the same values as the @ai2/tokens theme item, which the CLI merges into your globals.css automatically when you install any component. In your project the tokens are plain CSS variables: override them and every component follows, in both modes.
ai2 deliberately reuses the standard shadcn variables (--primary, --muted, --border…) for everything they already cover. Your existing shadcn theme keeps working. Only the extensions below are ai2-specific.
Tone tokens
| Token | Purpose |
|---|---|
| --brand / --brand-foreground | Primary product tone: ai2's own magenta (hue 331). Override one variable to swap it for your brand. |
| --brand-soft / --brand-soft-foreground | Low-emphasis brand surfaces: soft buttons, badges, hovers. |
| --success, --warning, --danger, --info (+ -foreground) | Solid semantic tones for buttons, badges, alerts, progress. |
| --*-soft / --*-soft-foreground | Soft pair for every semantic tone: tinted backgrounds with readable text. |
Surface tokens
| Token | Purpose |
|---|---|
| --surface-2 | First elevation above the page background: section bands, code blocks, tiles. |
| --surface-3 | Second elevation: nested surfaces inside surface-2. |
| --field-border | Border of every form control (input, textarea, select, checkbox, radio, OTP, file input). Kept at 3:1 against the page in both themes, because the border is the only cue that says where a field is (WCAG 1.4.11). Deliberately separate from --border, which stays faint for decorative rules, and from --input, which shadcn also uses as a fill. |
Motion tokens
| Token | Purpose |
|---|---|
| --motion-fast | 150ms: hovers, color transitions. |
| --motion-base | 200ms: reveals, toggles. |
| --motion-slow | 300ms: overlays, larger movement. |
| --motion-ease | cubic-bezier(0.2, 0, 0, 1), the single easing curve. |
In Tailwind, use them as duration-(--motion-fast) and ease-(--motion-ease).
Using tokens
<Button tone="brand">Uses --brand</Button>
<Alert tone="success">Uses --success + --success-soft</Alert>
<div className="bg-surface-2 rounded-xl p-6">Uses --surface-2</div>
<div className="transition-colors duration-(--motion-fast)">…</div>Building your own theme
ai2 ships its own magenta brand, but it is just a default. To recolor, override the variables after the ai2 token block - no component changes needed:
/* globals.css - after the ai2 token block */
:root {
--brand: oklch(0.55 0.2 265); /* indigo brand */
--brand-foreground: oklch(0.985 0 0);
--brand-soft: oklch(0.93 0.05 265);
--brand-soft-foreground: oklch(0.4 0.18 265);
}
.dark {
--brand: oklch(0.7 0.16 265);
--brand-foreground: oklch(0.15 0.02 265);
}Ready-made overlays
A theme is a registry:theme item: CSS variables only, no component changes. Install one and every component reading the brand tone follows, in both modes. Semantic tones (success, warning, danger, info) stay untouched.
ai2 Magenta is the built-in default (hue 331, light and dark). It ships automatically with @ai2/tokens when you install any component, so there is nothing extra to add.
Indigo is a free overlay that recolors the brand channel in both modes:
npx shadcn@latest add @ai2/theme-indigoTo dial your own values against real components and copy the result, use the theme generator.
Dark mode
Every token defines a light and a dark value; dark mode is the .dark class on <html>. This site defaults to the system preference and persists your toggle in localStorage. Any mechanism that sets the class works (next-themes included).