Migrate · 2 of 3
Move your tokens to Hoverlab’s
@theme block and which is not.What blocks read
A block never writes a colour. It writes bg-card, text-muted-foreground, border-border. For those to mean anything your project needs two things: a CSS variable for each name, and a way for Tailwind to turn the name into a utility. The names are:
--background --foreground --card --card-foreground --popover --popover-foreground --primary --primary-foreground --secondary --secondary-foreground --muted --muted-foreground --accent --accent-foreground --destructive --destructive-foreground --border --input --ring
Plus --radius, which the corner utilities derive from, and dark mode as a .dark class on an ancestor — the token files switch values on it.
Two colour formats — do not mix them
| HSL channels | Finished colours | |
|---|---|---|
| Looks like | --primary: 243 75% 59%; | --primary: oklch(0.52 0.19 250); |
| Used as | hsl(var(--primary)) in the Tailwind mapping | var(--primary) in the Tailwind mapping |
| Emitted by | The scaffolded templates’ globals.css, and the token export’s tokens.css | The registry’s @hoverlab/hoverlab and preset items, and the free theme generator |
The mapping and the variables have to agree. hsl(var(--primary)) around an oklch(…) value is not a colour, so the declaration silently does not apply — no error, the button is just the wrong colour or none. The reverse fails the same way. When a block looks unstyled or oddly transparent after a token change, this is the first thing to check.
Map your names onto ours
These pairings are suggestions — your names will differ. The right-hand column is what blocks actually read.
| If yours is… | Hoverlab reads | Used for |
|---|---|---|
| page background | background | The colour behind everything. |
| body text | foreground | Default text on the page background. |
| surface, panel, card | card | Raised areas. Pair with card-foreground for the text on them. |
| brand, accent, action | primary | Buttons and links. Pair with primary-foreground for text on top. |
| text on the brand colour | primary-foreground | The label inside a primary button. |
| subdued fill, chip, code well | muted | Quiet backgrounds. muted-foreground is the secondary-text colour. |
| secondary text | muted-foreground | Captions, helper text, placeholder text. |
| divider, hairline | border | Rules and card outlines. |
| form-field outline | input | Usually the same value as border. |
| danger, error | destructive | Delete buttons and error text. |
| focus outline | ring | The focus ring. Usually the brand colour. |
The least invasive route keeps your variables where they are and adds Hoverlab’s names beside them, defined in terms of yours. No value is copied, so there is still one place to change a colour.
:root {
/* Left: the names Hoverlab blocks read. Right: yours. */
--background: var(--surface-0);
--foreground: var(--text-1);
--card: var(--surface-1);
--card-foreground: var(--text-1);
--primary: var(--brand-600);
--primary-foreground: var(--on-brand);
--muted: var(--surface-2);
--muted-foreground: var(--text-2);
--border: var(--line);
--input: var(--line);
--ring: var(--brand-600);
/* …and the rest of the list above, until none is missing. */
}Aliasing keeps whatever format your variables are in, so the Tailwind mapping below has to match it: channels take hsl(var(--x)), finished colours take var(--x).
Which export file is what
The design-system export produces these. The palette on /design-system is live for anyone; downloading the files is a Pro feature.
| File | What it is | A Tailwind v4 @theme block? |
|---|---|---|
tokens.css | Plain CSS variables on :root and .dark, as HSL channels. | Not for colours. It carries an @theme block only for the shape: if you moved the spacing or type-scale sliders it adds --spacing and the --text-* ramp there, which v4 reads and v3 ignores. |
tailwind-theme.css | Maps each token to a utility, as --color-{name}: hsl(var(--{name})), plus a radius scale and a class-based dark variant. | Yes — an @theme inline block. Tailwind v4 only. |
tailwind-theme.v3.ts | A theme.extend object for tailwind.config. | No. A JavaScript config. Tailwind v3 only. |
tokens.light.json / tokens.dark.json | W3C design tokens: colour, radius, spacing and type, one file per mode. | No. For design tools and token pipelines. |
style-dictionary.config.mjs | Builds the two design-token files into CSS and JS with Style Dictionary. | No. For teams feeding other platforms. |
figma-variables.json / push-figma-variables.mjs | One Figma collection with Light and Dark modes, and the script that sends it. | No. Figma Enterprise plan only. |
hoverlab.config.json | The brand as four numbers, for the CLI. | No. |
You need tokens.css plus one of the two Tailwind files. The pair does nothing apart: the first declares --primary, the second is what makes bg-primary a class.
Wiring it into Tailwind v4
@import "tailwindcss";
@import "./tokens.css";
@import "./tailwind-theme.css";All three are @import rules, and CSS ignores an import that comes after any other rule — so they go at the very top. If your project already declares a dark variant, delete the @custom-variant dark line from tailwind-theme.css.
If your variables are finished colours instead — from the free generator, or your own — the mapping is the one without hsl(). The generator writes it for you; by hand it is one line per token:
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
/* one line per token, through ring */
}Wiring it into Tailwind v3
Merge the exported extend keys into your config rather than replacing it, and keep darkMode on class so the .dark block applies.
import type { Config } from 'tailwindcss'
import brand from './tailwind-theme.v3'
const config: Config = {
darkMode: 'class',
theme: {
extend: {
colors: { ...brand.extend.colors /* , your own */ },
borderRadius: { ...brand.extend.borderRadius },
},
},
}
export default configAnd put the :root / .dark blocks from tokens.css in your stylesheet. If it has an @theme block at the bottom, delete it: v3 has no such rule.
For your designers
tokens.light.json and tokens.dark.json are W3C design-token documents with hex values, one per mode, because DTCG has no settled syntax for modes and tools disagree about it. Import each into a variables plugin that reads DTCG, the dark file as a second mode on the same collection. The export’s own README names the importers it was written against.
On a Figma Enterprise plan, figma-variables.json skips the two-import step: it is the body of Figma’s Variables API, with Light and Dark already modes of one collection, and push-figma-variables.mjs sends it. Figma only allows that call on Enterprise, so the DTCG files stay the route for every other plan.
What the CLI does with a brand
Put hoverlab.config.json in your project root and npx hoverlab add tints effects to match it. Blocks and pages ignore the file entirely: they follow your tokens, which is the whole point of this page. The effect tinting is a hue rotation and a saturation shift, an approximation that moves every colour in the effect, including deliberately neutral ones. An explicit hue or saturation flag on the command overrides it.
A separate job, for AI tools rather than your build: npx hoverlab dna --brand indigo --out DESIGN.md writes the design system as one document to paste into an agent. --brand takes a preset id (emerald, indigo, rose, amber, cyan, violet, lime, orange, magenta, sky, crimson, teal), not an arbitrary colour, and the command changes nothing in your project. --out writes a file instead of printing.
If it does not look right
| You see | Usually |
|---|---|
| Blocks render with no colour at all | The utilities do not exist: the Tailwind mapping is missing or not imported. bg-card is not a class until it is. |
| Some colours right, some black or transparent | Mixed formats: a channels variable behind var(), or a finished colour inside hsl(). |
| Dark mode never switches | Nothing puts .dark on an ancestor, or v3 has darkMode unset, or v4 has no dark variant. |
| Spacing or type looks off after a v4 import | The @theme shape block in tokens.css changed --spacing or the type ramp for your whole project, not just the blocks. Delete the block to undo it. |
npx shadcn add @hoverlab/preset-console.Something wrong or missing here? Browse the catalog — every page in it links back to the source it documents.