Design Tokens
A design token in ChaiBuilder is a named bundle of utility classes. Instead of typing
inline-flex items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow hover:bg-primary/90 on every button, you apply one token
called Button-Primary. Change the token once and every block that uses it updates.
Tokens are a styling shortcut, not a separate style system. When a page renders, each token expands back into the classes it stands for. The published HTML contains ordinary utility classes, so nothing about hosting, caching, or CSS output changes.
Tokens vs the theme
These are two different layers, and they work best together.
| Design tokens | Theme | |
|---|---|---|
| What it holds | Class bundles: Button-Primary, Card, Input |
Values: colors, fonts, border radius |
| What it produces | Utility classes on the element | CSS variables in <style id="theme-variables"> |
| Scope | Site-wide | Site-wide |
| Who changes it | Whoever owns component styling | Whoever owns brand |
The built-in tokens are written against theme colors - bg-primary, text-card-foreground,
border-input. That is the seam. Change the theme's primary color and every button token
recolors without anyone touching a token. Change a token and the shape of the component
changes without touching the theme.
What ships built in
45 built-in tokens cover the common UI primitives. They carry shadcn/ui class values, which is why blocks styled with them look consistent out of the box.
| Group | Tokens |
|---|---|
| Button | Button, Button-Primary, Button-Destructive, Button-Outline, Button-Secondary, Button-Ghost, Button-Link, Button-Icon, Button-Small, Button-Large |
| Input | Input, Input-Small, Input-Large |
| Textarea | Textarea, Textarea-Small, Textarea-Large |
| Badge | Badge, Badge-Secondary, Badge-Destructive, Badge-Outline, Badge-Active, Badge-Inactive |
| Alert | Alert, Alert-Destructive, Alert-Title, Alert-Description |
| Card | Card, Card-Header, Card-Title, Card-Description, Card-Content, Card-Footer |
| Toggle | Toggle, Toggle-Outline, Toggle-Small, Toggle-Large |
| Form | Label, Checkbox, Switch, Switch-Thumb |
| Feedback | Skeleton, Progress, Progress-Indicator |
| Layout | Separator, Separator-Vertical |
For example, Card is rounded-xl border bg-card text-card-foreground shadow and
Button-Primary is bg-primary text-primary-foreground shadow hover:bg-primary/90.
Applying a token
Tokens are applied from the Classes section of the block settings panel, the same place you type utility classes by hand.
- Select a block.
- Open Classes in the settings panel.
- Start typing a token name -
ButsurfacesButton-Primary,Button-Outline, and the rest. Tokens are suggested above regular utility classes, and an empty input lists every token. - Pick one. It appears as a tinted chip with a component icon, sorted ahead of the plain classes. Hover the chip to see the classes it expands to.
You can mix tokens and hand-written classes on the same block. That is the normal way to
work: apply Button-Primary for the shape and color, then add w-full or mt-6 for the
one-off bits.
A hand-written class beats the token. Token classes are merged first, so adding
bg-emerald-600 next to Button-Primary gives you an emerald button rather than a fight
between two background classes.
Creating a token
Open Design Tokens from the header of the Classes section.
- Click Add.
- Give it a name. Names accept letters, digits, and single hyphens -
Hero-CTAis valid,Hero_CTAandHero--CTAare not. Spaces you type are converted to hyphens as you go. Maximum 75 characters. - Enter the classes, with the same autocomplete you get in the Classes panel.
- The preview pane on the right renders sample text with the classes applied.
- Save.
The token is now available in every block's class input, on every page of the site.
Composing tokens
A token's value can include another token. Hero-CTA can be Button-Primary plus
w-full sm:w-auto text-base, and it stays in sync when Button-Primary changes.
Nested tokens are resolved recursively, and a reference cycle resolves to nothing rather than hanging. Token names are not suggested inside the token editor's class input, so composing is deliberate rather than accidental.
Editing and overriding built-ins
Built-in tokens can be edited. Their names are locked, because block styles across your site refer to them, but the class value is yours to change. An edited built-in shows an Edited badge, and Reset to Built-in restores the shipped value.
Overriding is usually the right move when your buttons differ from the shadcn defaults.
Editing Button-Primary once updates every button on the site, including ones inside
partials, without any find-and-replace.
Seeing where a token is used
The token detail view has three usage tabs:
- Affected on this page - blocks in the page you have open, clickable to select them
- Affected on other pages - other pages that use the token; clicking one saves the current page and navigates there
- Affected on partial blocks - global/partial blocks that use the token
Usage on other pages comes from an index that is written every time a page is saved. A page that has not been saved since the token was applied will not appear until it is.
Archiving and deleting
Archive takes a custom token out of the picker. It is the safe operation: existing blocks keep the token and keep rendering exactly as before, and the token can be brought back. Use it when a token should not be applied to anything new.
Delete is only available for a token that is already archived, and it is permanent. Blocks that still reference a deleted token keep the reference, but it resolves to nothing - those blocks lose that styling silently, with no error and no visible warning on the page. Work through the usage tabs before deleting.
Renaming is safe. Blocks store an internal id, not the name, so renaming a token does not orphan anything. This is the opposite of how fonts behave, where the family name is the stored value.
Publishing
Design tokens are site-level data, like the theme. Edits save to the draft immediately and show up in the builder and in draft preview right away, but the live site only picks them up after you publish. Tokens appear as a Design Tokens entry in the publish list.
Permissions
Token operations are permission gated: reading, creating, editing, and deleting are separate grants. A role without read access does not see the Design Tokens link at all, so the Classes panel behaves like a plain class input. See Roles and permissions.
Turning the feature off
Design tokens can be disabled for a project with the designTokens feature flag in the
server config, which hides the suggestions and the manager. The label can also be renamed -
if your team calls them "styles" or "presets", the UI can say that instead. See
Configuration.
Developer notes
Tokens are stored as a single JSON object on the site record, keyed by an opaque id:
type ChaiDesignTokens = {
[uniqueId: string]: {
name: string
description?: string
value: string
archived?: boolean
}
}
Ids are prefixed dt#. Built-ins use readable ids such as dt#btn-primary; tokens you
create get a generated id. On a block, a styling prop looks like
#styles:<base classes>,<your classes>, and token ids live in the second segment.
Resolution happens before Tailwind compiles the page CSS. If you render blocks through a custom pipeline rather than the standard page route, apply tokens yourself first:
import { applyDesignTokens } from 'chaipro/utils'
const blocks = applyDesignTokens(page.blocks, settings.designTokens)
The built-in set is merged in automatically, so you only pass the site's own tokens.
applyDesignTokens rewrites the blocks it is given in place, so pass a copy if the same
array is used elsewhere.
The standard render path - RenderChaiBlocks, the layout renderer, and page style
generation - already does this. You do not need to call it for normal pages.
There is no API for registering additional built-in tokens from application code. The built-in set is fixed; project-specific tokens are created through the builder and stored with the site.
Gotchas
A custom token can shadow a built-in name. Duplicate-name validation only checks your
own tokens, not the built-in set. Creating a token called Button-Primary is allowed, but
typing that name in the class input resolves to the built-in, leaving your token
unreachable by name. Prefix project tokens - Acme-Button-Primary - if you want a variant
rather than an override. To change the built-in itself, edit it directly.
Class values are not validated. Any string is accepted. A typo like bg-primry
produces no error and no style. Use the preview pane to confirm.
Nothing warns you before a token disappears. Delete removes styling from every block that used it, and the block's class list falls back to showing the raw id. The archive step exists so this is a two-stage decision - keep it that way.
Tokens are site-wide, not per-page. There is no page-level token scope. A token edit affects every page once published.
Rapid edits can drop a save. Token edits are debounced and a save in flight is not queued behind another. After a burst of edits, pause a second and reopen the manager to confirm the values stuck.
Verify
- The token appears in the Classes autocomplete on any block.
- Applying it changes the canvas immediately.
- The usage tabs list the blocks you expect.
- On the published page, view source and confirm the expanded utility classes are on the
element - a
dt#string should never appear in shipped HTML.
Related
- Theming - the color, font, and radius values tokens build on
- Blocks - where styling props live
- Custom fonts - registering font families for the theme
- Configuration - feature flags and labels

