Theming & Accent Colors
How the floating ThemePicker works, how to add new accent colors, and how to lock a single brand color.
How the accent system works
Zutra uses a CSS custom property (--accent) that drives every accent-colored element in the UI: buttons, links, the highlighted pricing card’s halo, the focus ring on the ThemePicker, and so on.
When the user picks an accent from the floating ThemePicker, it:
- Updates
document.documentElement.style.setProperty('--accent', value)— immediate visual change - Persists the choice in
localStorageunder the keyzutra-accent - Re-applies it on every page load via a small inline script in
SiteHead.astro
This means the choice is per-browser, per-user — it doesn’t require any server state.
The default accents
src/config/features.ts defines the accent presets:
export const ACCENTS = [
{ id: 'indigo', label: 'Indigo', value: '#6366f1' },
{ id: 'violet', label: 'Violet', value: '#8b5cf6' },
{ id: 'rose', label: 'Rose', value: '#f43f5e' },
{ id: 'amber', label: 'Amber', value: '#f59e0b' },
{ id: 'emerald', label: 'Emerald', value: '#10b981' },
{ id: 'cyan', label: 'Cyan', value: '#06b6d4' },
{ id: 'zinc', label: 'Zinc', value: '#e4e4e7' },
];
The picker shows these as a row of color swatches in the bottom-right corner of every page.
Adding a new accent
- Open
src/config/features.ts - Add a new entry to
ACCENTS:
{
id: 'brand', // unique slug
label: 'Brand Purple', // shown in picker tooltip
value: '#7c3aed', // the actual hex
},
- Save. Hot-reload will pick it up — the new swatch appears in the picker.
No other code changes are needed. Every element that uses var(--accent) will reflect the new color.
Locking a single brand color
If your product has a strict brand color and you don’t want users picking accents:
- Open
src/config/features.ts - Set
FEATURE_FLAGS.themePicker: false - Open
src/styles/global.css - Edit the
:rootblock:
:root {
--accent: #your-brand-color;
--accent-hover: #lighter-shade-for-hover;
--accent-fg: #ffffff; /* foreground color on accent backgrounds */
}
The picker disappears, and the brand color is baked in.
How --accent is used
Run grep -r "var(--accent)" src/ to see every usage. The main ones:
src/styles/global.css— base.btn-accentclass,--accent-hovertoken.card-border-highlighted— the highlighted pricing card’s halo usescolor-mix(in srgb, var(--accent) 14%, transparent)<Badge variant="accent">— accent-colored badges- Pricing CTA button —
background: var(--accent); color: var(--accent-fg); - ThemePicker swatches and active ring
Foreground color on accent backgrounds
When you put white text on a colored background, you need contrast. The --accent-fg CSS variable exists exactly for this:
.my-cta {
background: var(--accent);
color: var(--accent-fg);
}
The template’s pricing card CTA uses this pair. Pick your --accent-fg based on the brightness of --accent:
--accent brightness |
--accent-fg |
|---|---|
Dark (e.g. #1e40af) |
#ffffff |
Light (e.g. #fde047) |
#000000 |
| Medium | test both, pick the one with better contrast |
Use the WebAIM contrast checker to verify WCAG AA (4.5:1 for text).
Adding multiple accent slots
If you need more than one accent color in your UI (e.g. one for primary, one for success, one for warnings), define them as additional CSS variables in :root:
:root {
--accent: #6366f1;
--accent-hover: #818cf8;
--accent-fg: #ffffff;
--success: #22c55e;
--warning: #f59e0b;
--danger: #ef4444;
}
These already exist in @theme — see the Zutra palette at the top of global.css. Use them as Tailwind utilities: text-success, bg-warning/10, etc.
Disabling animations on the picker
If you want the swatch row to feel more “industrial” (less bounce), edit src/components/ThemePicker.astro and remove the transition-* classes. Or set FEATURE_FLAGS.themePicker: false to hide it entirely.
Next steps
- Customization — broader customisation beyond colors
- Configuration Reference — full
FEATURE_FLAGSandACCENTSshape - Components — extending the ThemePicker component