Color Presets
10 built-in accent presets that override the primary color tokens at runtime — no theme rebuild needed. Extend with your own brand colors.
pnpm add @structyl/themesBuilt-in presets
COLOR_PRESETS is a typed as const array of all 10 built-in accent colors. Import it for rendering your own picker UI.
import { COLOR_PRESETS } from '@structyl/themes';
// [
// { id: 'structyl', name: 'Structyl', hex: '#5754a3' },
// { id: 'indigo', name: 'Indigo', hex: '#6366f1' },
// { id: 'ocean', name: 'Ocean', hex: '#0284c7' },
// { id: 'rose', name: 'Rose', hex: '#e11d48' },
// { id: 'forest', name: 'Forest', hex: '#16a34a' },
// { id: 'sunset', name: 'Sunset', hex: '#f97316' },
// { id: 'violet', name: 'Violet', hex: '#7c3aed' },
// { id: 'slate', name: 'Slate', hex: '#475569' },
// { id: 'ember', name: 'Ember', hex: '#be123c' },
// { id: 'zinc', name: 'Zinc', hex: '#71717a' },
// ]useColorPreset
The primary way to work with presets in React. Must be used inside a ThemeProvider. Persists the selection to localStorage and automatically re-applies the preset after every theme or color-mode change.
Click a preset — every primary-color token on this page updates live.
'use client';
import { useColorPreset, COLOR_PRESETS } from '@structyl/themes';
function AccentPicker() {
const { activeId, setPreset, clearPreset } = useColorPreset();
return (
<div className="flex gap-2">
{COLOR_PRESETS.map(({ id, name, hex }) => (
<button
key={id}
onClick={() => setPreset(id, hex)}
title={name}
style={{ background: hex }}
className="h-7 w-7 rounded-full"
aria-pressed={activeId === id}
/>
))}
{activeId && (
<button onClick={clearPreset}>Reset</button>
)}
</div>
);
}Options
| Option | Type | Default | Description |
|---|---|---|---|
| extraPresets | ColorPreset[] | [] | Additional presets to add on top of the 10 built-ins. Built-ins always come first. |
| storageKey | string | 'structyl-color-preset' | localStorage key used to persist the active preset. Override when you need multiple independent pickers on the same origin. |
Return value
| Property | Type | Description |
|---|---|---|
| presets | ColorPreset[] | All available presets (built-ins + extraPresets). |
| activeId | string | null | ID of the active preset, or null when using the default theme. |
| activePreset | ColorPreset | null | Full preset object for the active ID. |
| setPreset | (id, hex) => void | Apply a preset — updates CSS vars and persists to localStorage. |
| clearPreset | () => void | Remove the override and restore the base theme's primary colors. |
createColorPreset
Factory for creating typed preset objects. Pass the result to extraPresets in useColorPreset.
A custom #ff5500 preset created with createColorPreset.
import { createColorPreset, useColorPreset } from '@structyl/themes';
const brandPreset = createColorPreset('brand', 'Brand Blue', '#1a6cf0');
const seasonalPreset = createColorPreset('holiday', 'Holiday Red', '#c0392b');
function MyPicker() {
const { presets, activeId, setPreset, clearPreset } = useColorPreset({
extraPresets: [brandPreset, seasonalPreset],
});
// presets = [...COLOR_PRESETS, brandPreset, seasonalPreset]
}Imperative API
Use these utilities outside of React (e.g. in event handlers, server actions, or non-React contexts). Both are no-ops in SSR environments.
import { applyColorPreset, clearColorPreset } from '@structyl/themes';
// Apply any hex color as the primary accent
applyColorPreset('#6366f1');
// Sets --color-primary, --color-ring, --color-primary-hover,
// --color-primary-active, --color-primary-fg on <html>
// Remove the override, restoring the active theme's base colors
clearColorPreset();Types
import type { ColorPreset, ColorPresetId } from '@structyl/themes';
// Interface for any preset object (built-in or custom)
interface ColorPreset {
id: string;
name: string;
hex: string;
}
// Union of all built-in preset IDs
type ColorPresetId =
| 'structyl' | 'indigo' | 'ocean' | 'rose' | 'forest'
| 'sunset' | 'violet' | 'slate' | 'ember' | 'zinc';Next.js App Router
Client Component required
useColorPreset uses React state and effects, so any component that calls it must be marked 'use client'. The imperative applyColorPreset / clearColorPreset functions are safe to import from any module — they guard against SSR automatically.
'use client'; // required
import { useColorPreset } from '@structyl/themes';
export function AccentPicker() {
const { activeId, setPreset } = useColorPreset();
// ...
}How it works
Presets work by overriding five CSS custom properties on document.documentElement after the ThemeProvider applies its base tokens:
/* Applied by applyColorPreset('#6366f1') */
:root {
--color-primary: 239 84% 67%;
--color-ring: 239 84% 67%;
--color-primary-hover: 239 84% 73%;
--color-primary-active: 239 84% 59%;
--color-primary-fg: 0 0% 100%; /* white or dark, auto-calculated via WCAG */
}Because useColorPreset watches theme and resolvedMode from useTheme, it automatically re-applies the override whenever the user switches themes or color modes — the base theme resets first, then the preset overwrites the five primary vars.