Tailwind CSS v4
Map every semantic token to Tailwind v4 utilities and theme variables.
@themvora/tailwindTailwind CSS v4v2.0.0Stable1Install
pnpm add @themvora/core @themvora/tailwind tailwindcss2Set up
@import "tailwindcss";
@import "@themvora/tailwind";3Add a small UI
Design system
ActiveTokens resolve at runtime, so this card follows whichever theme is active.
<div class="grid max-w-sm gap-2.5 rounded-xl border border-border bg-card p-4 text-card-foreground shadow-sm">
<div class="flex items-center justify-between gap-2">
<h3 class="text-[15px] font-semibold">Design system</h3>
<span class="inline-flex items-center gap-1.5 text-xs font-medium text-success">
Active
</span>
</div>
<p class="text-[13px] text-muted-foreground">
Tokens resolve at runtime, so this card follows whichever theme is active.
</p>
<p class="text-xs text-muted-foreground">Updated 2 minutes ago</p>
<button
type="button"
class="justify-self-start rounded-md bg-primary px-3.5 py-2 text-[13px] font-semibold text-primary-foreground hover:brightness-110"
>
View themes
</button>
</div>Tailwind reads the same tokens through the @theme block in @themvora/tailwind/theme.css, so the utilities resolve to the active theme instead of a palette baked into the build.
4Style it with Themvora tokens
/* Pick one of the two document rules below. Both rely only on the
required `background` and `foreground` colours. */
/* 1. Tailwind utility approach */
html,
body {
@apply bg-background text-foreground;
}
/* 2. Direct CSS-variable approach */
html,
body {
background: var(--theme-color-background);
color: var(--theme-color-foreground);
}.themvora-example-card and its children read Themvora tokens, so switching the active theme repaints them — no re-render, and no per-theme CSS.The utilities in the previous step read the same required colour roles — background, foreground, card, cardForeground, border, mutedForeground, success, primary, primaryForeground and ring — through the @theme block in @themvora/tailwind. This step only paints the document; the component styles are utilities, not a second stylesheet, and no typography token is involved.
Theme configuration
@import "tailwindcss";
@import "@themvora/tailwind";
body {
@apply bg-background text-foreground;
}What's Available
@themvora/tailwind ships 7 exports in 2 categories. Click an export to see what it does and jump to the code that uses it.
Three pages cover this package, and they do different jobs: this index says what exists, the examples page shows each export in code with the problem it solves, and the API reference gives exact signatures and return types.
Use Cases
Short snippets you can drop into an existing app. For the same problems worked through end to end — problem, APIs, expectations, limitations — see the Tailwind CSS v4 examples.
Map tokens to Tailwind
Import the plugin so theme tokens become Tailwind v4 utilities.
@import "tailwindcss";
@import "@themvora/tailwind";Dark variant
The plugin wires the .dark class so dark: works out of the box.
@custom-variant dark (&:where(.dark, .dark *));
/* bg-background, text-foreground, bg-primary, ring-ring, ... are all tokens */
.card {
@apply bg-card text-foreground border border-border rounded-lg;
}Use tokens as utilities
Every semantic token maps to --color-*, --radius-*, --shadow-* and friends.
export function Card() {
return (
<div className="bg-card text-card-foreground rounded-lg shadow-md border border-border">
<button className="bg-primary text-primary-foreground hover:bg-primary/90">
Primary action
</button>
</div>
);
}Keep the dark class in sync
synchronizeDarkClass bridges a themvora runtime with Tailwind's dark variant.
import { synchronizeDarkClass } from "@themvora/tailwind";
import { getProviderRuntime } from "@themvora/web";
const runtime = getProviderRuntime();
synchronizeDarkClass(runtime.store.get());Smooth theme transitions
Transitions are a runtime option, not CSS you write. Configure them where the runtime is created — here the provider element's transition attribute.
<themvora-provider
default-theme="light"
transition='{"enabled": true, "duration": 300, "easing": "ease-in-out"}'
>
<themvora-toggle></themvora-toggle>
</themvora-provider>The dark variant and the token map
Tailwind's dark: variant has to agree with Themvora's resolved mode, and each utility needs a token behind it. These two files are that contract.
import { synchronizeDarkClass } from "@themvora/tailwind";
import { createThemeRuntime, getBuiltInThemes } from "@themvora/core";
// Tailwind is build-time only, so it has no provider and no hook. Create the
// runtime yourself and let Tailwind's own helper mirror it onto the `.dark`
// class, which is what the `dark:` variant keys off.
const runtime = createThemeRuntime({
themes: getBuiltInThemes(),
defaultTheme: "plum-light",
});
runtime.store.subscribe((theme) => synchronizeDarkClass(theme));
// Switching is the runtime's, not Tailwind's.
export function setMode(mode: "light" | "dark" | "system") {
runtime.selection.setMode(mode);
}
export function toggleTheme() {
runtime.selection.toggleTheme();
}Keeping Tailwind's dark class in step with the runtime's resolved mode, so dark: utilities and Themvora tokens flip together.
/* @themvora/tailwind maps every semantic token onto a Tailwind v4 utility.
The ones you will reach for most:
.bg-background .text-foreground
.bg-card .text-card-foreground
.bg-popover .text-popover-foreground
.bg-primary .text-primary-foreground
.bg-secondary .text-secondary-foreground
.bg-muted .text-muted-foreground
.bg-accent .text-accent-foreground
.bg-destructive .text-destructive-foreground
.bg-success .text-success-foreground
.border-border .bg-input .ring-ring
Plus the token-driven scales, all reading --theme-* variables:
.rounded-lg --theme-radius-lg
.p-4 .gap-2 .mt-6 --theme-spacing-*
.shadow-md --theme-shadow-md
.border-2 --theme-border-width-*
.z-40 --theme-z-index-*
Because the variables are live, Tailwind's `dark:` variant is optional —
the runtime already swapped the values behind these utilities. */The token → utility map, as a reference rather than something to copy.
The complete runnable app behind this guide is examples/apps/tailwind — listed with the other example apps.
Related documentation
Every export in @themvora/tailwind, with code and use cases
Build your own color palettes and semantic tokens
Complete @themvora/tailwind documentation
Upgrade from previous versions
Check framework and library version support