Theming
Three themes ship from one semantic token contract. Colors are HSL triplets exposed as CSS custom properties, so they work with or without Tailwind.
Live theme playground
Switch the theme applied to these previews in real time. Only this surface changes — the rest of the page keeps your chosen theme.
Live theming
Invoice #A-2291
Due in 14 days
Semantic HSL tokens
Every visual decision goes through a --vexa-* token. Never hardcode a color; never repeat a raw value.
Values are stored as space-separated HSL triplets ("H S% L%") so one token drives solid fills, translucent overlays, and rings via hsl(var(--token) / <alpha>). In Tailwind v4 the tokens map to utilities like bg-vexa-surface, text-vexa-foreground, and border-vexa-border.
/* Plain CSS — HSL triplet + alpha */
.cta {
background: hsl(var(--vexa-primary));
color: hsl(var(--vexa-primary-foreground));
border-color: hsl(var(--vexa-border) / 0.6);
}
/* Tailwind v4 — mapped utilities */
<div class="rounded-vexa-md border border-vexa-border bg-vexa-surface text-vexa-foreground" />The three themes
Light is the default at :root. The dark themes activate through a single data attribute on <html> (or any scoped element).
- LightNeutral, editorial, high clarity.
Default at :root — no attribute needed. - ObsidianNear-black, low-emission, high contrast.
data-theme="obsidian" - GraphiteBalanced dark-gray surfaces for dense tools.
data-theme="graphite"
Token reference (Obsidian)
The semantic color tokens resolved in the default Obsidian theme. Foreground/background pairs are validated against WCAG 2.2 AA.
--vexa-background--vexa-foreground--vexa-surface--vexa-surface-raised--vexa-muted--vexa-muted-foreground--vexa-border--vexa-input--vexa-primary--vexa-primary-foreground--vexa-secondary--vexa-secondary-foreground--vexa-accent--vexa-accent-foreground--vexa-success--vexa-success-foreground--vexa-warning--vexa-warning-foreground--vexa-danger--vexa-danger-foreground--vexa-focus--vexa-ringCustomize
Because tokens are plain CSS variables, you override them per product without touching component source. Redefine a token at :root or under a [data-theme] block and every primitive follows.
/* Override tokens per product — no component edits needed. */
:root {
--vexa-primary: 262 60% 45%; /* your brand ink */
}
[data-theme="obsidian"] {
--vexa-primary: 262 70% 66%;
}