ChaiBuilder Logo

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.

  1. Select a block.
  2. Open Classes in the settings panel.
  3. Start typing a token name - But surfaces Button-Primary, Button-Outline, and the rest. Tokens are suggested above regular utility classes, and an empty input lists every token.
  4. 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.

  1. Click Add.
  2. Give it a name. Names accept letters, digits, and single hyphens - Hero-CTA is valid, Hero_CTA and Hero--CTA are not. Spaces you type are converted to hyphens as you go. Maximum 75 characters.
  3. Enter the classes, with the same autocomplete you get in the Classes panel.
  4. The preview pane on the right renders sample text with the classes applied.
  5. 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

  1. The token appears in the Classes autocomplete on any block.
  2. Applying it changes the canvas immediately.
  3. The usage tabs list the blocks you expect.
  4. 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.
  • 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

© ChaiBuilder. All rights reserved.