Themes
Customize the look and feel of your application with built-in themes, colors, and dark mode support.
Available Themes
Volt UI comes with several pre-built color themes. Each theme provides a unique palette for primary, secondary, accent, and semantic colors.
Volt
Default blue theme
Ember
Warm orange theme
Sage
Natural green theme
Dusk
Purple twilight theme
Glacier
Cool cyan theme
Theme Setup
Add the theme provider to your application configuration:
Color Options
'volt'- Blue (default)'ember'- Orange/Red'sage'- Green'dusk'- Purple'glacier'- Cyan
'sharp'- Small radius, flat shadows (default)'soft'- Large radius, diffuse shadows'brutal'- No radius, hard offset shadows, heavy borders'ghost'- No borders, no shadows'retro'- Y2K look
Runtime API
Two functions from @voltui/components control the theme at runtime. Both just set data-color, data-style and a .dark class on <html> — there is no hidden state, so you can inspect or override the result with plain DOM APIs at any time.
appConfig.providers to set the theme at bootstrap, on both server and client.SSR-safe. It reads Angular's DOCUMENT injection token rather than the global document, so the theme attributes are present in the server-rendered HTML itself — no flash of the default theme while the client bundle hydrates.
Browser-only by default (it no-ops if document is undefined). Pass an explicit doc argument to target a different document, e.g. in a test.
Dark Mode Strategy
Dark mode is class-based: adding .dark to <html> switches every component to its dark token values (see core.css's @custom-variant dark). Volt does not read prefers-color-scheme automatically — dark defaults to light unless you pass dark: true or call applyVoltTheme yourself, so the choice is always explicit and won't silently change if the visitor's OS theme changes.
The docs app itself persists the visitor's choice to localStorage and applies it from a small inline script in index.html's <head>, before any stylesheet loads — see src/app/components/theme-switcher.ts for a reference implementation if you want the same no-flash behavior client-side.
CSS Variables
Every preset sets the same set of semantic tokens as oklch() values. Components consume them through Tailwind utilities (bg-primary, text-muted-foreground) mapped via @theme inline in core.css — never a raw var(--primary). This is the full list a preset can define; see the two guides below for copy-paste templates.
/* Color tokens — one block per data-color, one per .dark[data-color] */ --background / --foreground --surface / --surface-foreground /* cards, popovers, dropdowns */ --muted / --muted-foreground --border / --input / --ring /* --input is the form-control border */ --primary / --primary-foreground --secondary / --secondary-foreground --destructive / --destructive-foreground /* shared by all colors, only in core.css */ --success / --success-foreground --warning / --warning-foreground --error / --error-foreground --info / --info-foreground /* Style tokens — one block per data-style */ --radius --border-width / --border-style --ring-width / --ring-offset-width --volt-shadow-sm / --volt-shadow / --volt-shadow-md / --volt-shadow-lg --spacing-component / --spacing-gap --volt-font-weight-base / --volt-font-weight-heading / --volt-font-weight-label
Create a Custom Color Preset
A color preset is just a pair of selectors keyed by data-color. Copy this template, replace 'mytheme' and the values, then import the file after @voltui/components/themes.css in your global stylesheet.
:root[data-color='mytheme'] {
--primary: oklch(0.58 0.2 265);
--primary-foreground: oklch(0.98 0 0);
--secondary: oklch(0.96 0.01 265);
--secondary-foreground: oklch(0.15 0.01 265);
--background: oklch(1 0 0);
--foreground: oklch(0.15 0.01 265);
--surface: oklch(1 0 0);
--surface-foreground: oklch(0.15 0.01 265);
--muted: oklch(0.96 0.005 265);
--muted-foreground: oklch(0.5 0.02 265);
--border: oklch(0.9 0.01 265);
--input: oklch(0.65 0.02 265);
--ring: oklch(0.58 0.2 265);
--success: oklch(0.5 0.14 150);
--success-foreground: oklch(0.98 0 0);
--warning: oklch(0.7 0.16 70);
--warning-foreground: oklch(0.2 0.05 70);
--error: oklch(0.55 0.22 25);
--error-foreground: oklch(0.98 0 0);
--info: oklch(0.55 0.14 240);
--info-foreground: oklch(0.98 0 0);
}
.dark[data-color='mytheme'] {
/* same tokens, tuned for a dark --background/--surface */
} Keep every foreground/background pair at 4.5:1 contrast or better (3:1 for --ring and --input against --background) — see scripts/contrast-audit.mjs in the repo for the exact check this project runs against its own presets. The Create Theme tool generates a starting point from a single accent color if you'd rather not hand-pick eleven tokens.
Create a Custom Style Preset
A style preset never touches color — only shape. It's keyed by data-style and applies to every color at once.
:root[data-style='mystyle'] {
--radius: 0.5rem;
--border-width: 1px;
--border-style: solid;
--ring-width: 2px;
--ring-offset-width: 2px;
--volt-shadow-sm: 0 1px 2px 0 oklch(0 0 0 / 0.05);
--volt-shadow: 0 1px 3px 0 oklch(0 0 0 / 0.1);
--volt-shadow-md: 0 4px 6px -1px oklch(0 0 0 / 0.1);
--volt-shadow-lg: 0 10px 15px -3px oklch(0 0 0 / 0.1);
--spacing-component: 1;
--spacing-gap: 0.5rem;
--volt-font-weight-base: 400;
--volt-font-weight-heading: 600;
--volt-font-weight-label: 500;
} Registration is the same as a color preset: put it in your global stylesheet after @voltui/components/themes.css, then set data-style="mystyle" via provideVoltTheme or applyVoltTheme.