88 semantic color tokens. One class per role, almost no dark-mode overrides.
Create UI ships a single color system built on semantic tokens. Instead of reaching for a raw color like red-600 and remembering to add a dark-mode override, you name the role a color plays (bg-error-base, text-body, border-primary-weak) and the system resolves the right value for the active theme.
There are 88 of them: ten five-step ramp families plus the static, background, text, overlay, and shadow groups. Across the whole component library only two files still need a dark: color override. Colors are the one foundation that does not scale with the viewport: they move on the light and dark axis instead.
The same markup, in both themes:
Invite your team
Everyone you add gets the same seat, billed once per member.
Seats renew monthly
You can remove a member at any time.
Invite your team
Everyone you add gets the same seat, billed once per member.
Seats renew monthly
You can remove a member at any time.
bg-statictext-strongesttext-bodyborder-lightbg-primary-base
Not one dark: in this markup.
Why semantic tokens
The same surface, written two ways:
The payoff:
- Intent over value.
bg-error-basesays what it is.bg-red-600makes the next reader guess. - Dark mode for free. Each token already carries a light and a dark value. You almost never write a
dark:color override again. - One place to retheme. Change a token mapping once and every component that uses it updates. The brand
primaryfamily is just an alias you can repoint.
Tokens are exposed as ordinary Tailwind color utilities, so they work everywhere a color does: bg-*, text-*, border-*, outline-*, fill-*, divide-*, and so on. That is why every swatch on this page copies the bare token name rather than a prefixed class.
The strength ramp
Most families share one vocabulary, ordered from lightest to most prominent:
weakest → weak → base → strong → strongest
weakest/weak: tinted backgrounds and subtle fills.base: the solid, default color (buttons, icons, indicators).strong/strongest: text, emphasis, and high-contrast detail on top of the weak fills.
Every swatch below is split on the diagonal: the light value fills the top-left, the dark value the bottom-right, with both hex values printed underneath. The step name under each swatch copies the token.
primary is an alias, so a theme can repoint the whole family to any hue without touching a single component.
Background and text
Neutral surfaces use a longer ramp built for layering, resolved from a zinc base for a refined, slightly cool gray. Lower steps are page and card backgrounds; higher steps are borders, dividers, and strong UI chrome. The ramp inverts in dark mode, which the split swatches show directly: weakest is the lightest surface in light mode and the darkest in dark.
Text has its own roles. strongest is for headings and high-emphasis copy, body for comfortable reading, and placeholder / disabled for de-emphasized states. A text token is only ever as good as its legibility, so these are shown as real type rather than as fills:
Status and brand families
Every family carries the same weakest → strongest ramp and is meant for one kind of meaning. Reach for the family that matches intent, not the one that happens to be the right hue. On a shared baseline the families compare directly, which is the question you actually have when choosing between warning and error.
The first is brand. The next four carry status. The last five are accents for product-specific meaning: a verified badge, a feature callout, a highlighted promotion, an away presence dot, a stable or neutral state.
Static colors
Static tokens are theme-aware foreground anchors for sitting on top of colored or inverted surfaces. static-white is white in light mode and flips to black in dark mode. static-black does the opposite. That flip is what keeps text legible on an inverted surface, since the surface itself changes with the theme.
Not a label color for brand fills
static-white is a foreground anchor for inverted surfaces, not the label color for a brand fill. On a fill that stays saturated in both themes, use plain text-white, which is exactly what Button does. Reaching for text-static-white there produces black text on an indigo button in dark mode.
Each static color also exposes alpha-* steps (static-white-alpha-16, static-black-alpha-24, ...) for translucent overlays such as spinner tracks and hairlines on top of color. They appear over a checkerboard in the token reference below so the transparency itself reads.
Overlay and interaction
These tokens are alpha tints used for state and depth, not solid fills.
scrim/scrim-strong: dimming behind modals, drawers, and popovers.hover/active: subtle wash for interactive states on the default surface, in either theme.hover-inverted/active-inverted: the same idea for a surface that is inverted relative to the active theme.
Each one is subtle by design, so scrim and hover are shown next to the untouched surface. The rest of the set behaves the same way, one step stronger or inverted:
Guidelines
Do reach for the semantic token that matches intent. Avoid raw primitives and manual dark: color overrides in product and site code.
- Use
basefor solid fills,weakest/weakfor tinted backgrounds,strong/strongestfor text and emphasis. - Reserve raw primitive scales (
bg-red-600) for the rare case a design genuinely needs a one-off hue with no semantic role. - Let the root
.darkclass do the work. If you find yourself writingdark:bg-*for color, there is almost always a token that already handles it.
Token reference
Every semantic token with the hex it resolves to in each theme. Search matches both the name and the value, so pasting a hex finds the token that paints it. Press / to jump to the search field, and click any swatch to copy its token name.
How it works
Every token lands in your globals.css when you set up Create UI.
A token flows through three layers before it reaches your markup.
1. Primitive scales. The raw palette: zinc, red, blue, indigo, and the rest, each with shades 50-950 plus alpha-* steps, alongside bare black and white that carry only alpha-* steps. They arrive as --color-* variables and are rarely used directly in product code. Two families are aliases a theme repoints: primary resolves to indigo, and neutral resolves to zinc.
2. Semantic tokens. Each maps a role to a light and a dark primitive, and remaps under .dark:
3. Tailwind utilities. An @theme block re-exports each variable so the token becomes an ordinary color utility:
Dark mode is driven by a .dark class on an ancestor (usually <html>). Everything inside it resolves the dark value automatically, so you set the theme once at the root and never thread it through components.