The Visual Website Builder
You Actually Own. React + Next.js, low-code, self-hosted. Your data, your infrastructure, every feature included.
© ChaiBuilder. All rights reserved.

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.
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.
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.
Tokens are applied from the Classes section of the block settings panel, the same place you type utility classes by hand.
But surfaces Button-Primary, Button-Outline, and the
rest. Tokens are suggested above regular utility classes, and an empty input lists every
token.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.
Open Design Tokens from the header of the Classes section.
Hero-CTA is valid,
Hero_CTA and Hero--CTA are not. Spaces you type are converted to hyphens as you go.
Maximum 75 characters.The token is now available in every block's class input, on every page of the site.
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.
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.
The token detail view has three usage tabs:
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.
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.
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.
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.
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.
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.
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.
dt# string should never appear in shipped HTML.