Themes & Customization·5 min read·

Tailwind v4 CSS variables and your theme preset

VeloCMS themes are built entirely on CSS custom properties injected via Tailwind v4's @theme engine. Understanding how presets map to CSS variables lets you extend themes precisely without fighting the system.

Every VeloCMS theme preset is a JSON file that defines semantic design tokens — colors, typography scales, border radii, spacing — as CSS custom properties. Tailwind v4's @theme engine reads these properties and generates utility classes automatically. When you switch themes, only the custom property values change; the Tailwind classes in your HTML stay the same. This is how the same blog template renders beautifully in all five presets.

How the preset JSON maps to CSS variables

A theme preset JSON object like { colors: { primary: 'oklch(0.55 0.22 250)' } } compiles to --color-primary: oklch(0.55 0.22 250) in the :root block. Tailwind v4's @theme engine then makes bg-primary, text-primary, border-primary, and ring-primary available as utility classes — all reading from the same CSS variable. Change the variable, every utility that references it updates automatically.

/* Generated from the theme preset JSON */
@theme inline {
  --color-background: oklch(0.98 0 0);
  --color-foreground: oklch(0.12 0 0);
  --color-primary: oklch(0.55 0.22 250);
  --color-muted: oklch(0.96 0.01 240);
  --color-muted-foreground: oklch(0.45 0.02 240);
  --font-sans: 'Inter', sans-serif;
  --radius: 0.75rem;
}

Extending the preset without modifying the source file

If you want to override a single token — say, change --radius from 0.75rem to 0.25rem for a sharper design — add a CSS override in Admin Themes Advanced CSS. The override goes in a :root block and takes precedence because it's injected after the preset in the stylesheet cascade. This is the right pattern for small tweaks. For a full custom palette, create a new theme preset JSON instead.

Semantic tokens vs. raw Tailwind classes

VeloCMS's templates always use semantic token classes — bg-background, text-foreground, bg-primary — rather than raw color classes like bg-indigo-600 or text-zinc-900. This is intentional: raw classes are hardcoded to a specific shade and don't respond to theme switching. If a template uses bg-indigo-600, switching from the Indigo preset to the Terminal preset doesn't change that element — it stays indigo. Semantic tokens switch automatically because they read from CSS variables.