Skip to content
Zutra HyperSaaS — 6 languages, auth UI, dashboard, 69 tests
Get it on Gumroad

Zutra v1.3.0 — 6 languages, auth UI, dashboard, 69 tests. Saves 80+ hours.

See what's included

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:

  1. Updates document.documentElement.style.setProperty('--accent', value) — immediate visual change
  2. Persists the choice in localStorage under the key zutra-accent
  3. 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

  1. Open src/config/features.ts
  2. Add a new entry to ACCENTS:
{
  id:    'brand',           // unique slug
  label: 'Brand Purple',    // shown in picker tooltip
  value: '#7c3aed',         // the actual hex
},
  1. 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:

  1. Open src/config/features.ts
  2. Set FEATURE_FLAGS.themePicker: false
  3. Open src/styles/global.css
  4. Edit the :root block:
: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-accent class, --accent-hover token
  • .card-border-highlighted — the highlighted pricing card’s halo uses color-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