Getting Started
tailwind-hue-theme
Section titled “tailwind-hue-theme”A Tailwind CSS v4 plugin that makes your entire color palette shift dynamically based on a single --brand-h CSS variable. Ship light, bring your own color wheel.
Includes an optional HuePicker widget — a floating circular color wheel for live hue selection with drag support, preset swatches, and localStorage persistence.
Features
Section titled “Features”- Single variable control — update
--brand-h(0–360) and your entire palette shifts: backgrounds, borders, text, and accents. - OKLCH color space — perceptually uniform, wide-gamut colors that look great at any hue.
- Slate + Indigo + Cyan scales all respond to
--brand-h. Slate uses low chroma so dark backgrounds stay dark; indigo follows the hue directly; cyan is offset by +40°. - Optional HuePicker widget — drag the circular color wheel, click a preset, or call
setHue()from code. - localStorage persistence — selected hue survives page reloads.
- Zero runtime dependencies — ships CJS + ESM +
.d.ts.
Installation
Section titled “Installation”npm install tailwind-hue-themeTailwind v4 plugin
Section titled “Tailwind v4 plugin”Add to your CSS:
@import "tailwindcss";@plugin "tailwind-hue-theme";That’s it. The plugin injects --brand-h: 250 (indigo) into :root and remaps
--color-slate-*, --color-indigo-*, and --color-cyan-* to OKLCH values that
track --brand-h at runtime.
Switch the palette live from JavaScript:
// Drop-in anywhere — no framework requireddocument.documentElement.style.setProperty('--brand-h', '155') // Emeralddocument.documentElement.style.setProperty('--brand-h', '10') // Rosedocument.documentElement.style.setProperty('--brand-h', '250') // Indigo (default)Override the default hue in CSS without JavaScript:
@plugin "tailwind-hue-theme";:root { --brand-h: 155; }JS config API (Tailwind v4 / v3)
Section titled “JS config API (Tailwind v4 / v3)”import { createHuePlugin } from 'tailwind-hue-theme'
export default { plugins: [ createHuePlugin({ defaultHue: 155, secondaryOffset: 50 }), ],}createHuePlugin(options?)
Section titled “createHuePlugin(options?)”| Option | Type | Default | Description |
|---|---|---|---|
defaultHue | number | 250 | Starting hue (0–360). Written to --brand-h. |
secondaryOffset | number | 40 | Degrees added to --brand-h for the cyan scale. |
buildTokens(options?)
Section titled “buildTokens(options?)”Returns the raw Record<string, string> of CSS custom properties so you can
inspect or test the generated values programmatically.
HuePicker widget
Section titled “HuePicker widget”import { HuePicker } from 'tailwind-hue-theme/widget'
const picker = new HuePicker()// → Floating color wheel appears in the bottom-right corner.Options
Section titled “Options”| Option | Type | Default | Description |
|---|---|---|---|
defaultHue | number | 250 | Initial hue (0–360) if no persisted value exists. |
cssVariable | string | '--brand-h' | CSS custom property updated on <html>. |
storageKey | string | 'tailwind-hue-theme' | localStorage key for persistence. |
container | HTMLElement | document.body | Element the widget is appended to. |
onChange | (hue: number) => void | — | Called every time the hue changes. |
presets | HuePreset[] | 6 built-in | Preset swatches. Pass [] to disable. |
Methods
Section titled “Methods”| Method | Description |
|---|---|
getHue(): number | Returns the current hue (0–360). |
setHue(n: number) | Programmatically set hue. Normalises values outside 0–360. |
destroy() | Removes all DOM nodes and the injected <style> tag. |
Preset format
Section titled “Preset format”interface HuePreset { label: string // Shown in aria-label and title hue: number // 0–360}Built-in presets
Section titled “Built-in presets”| Name | Hue |
|---|---|
| Indigo | 250 |
| Teal | 195 |
| Purple | 295 |
| Rose | 10 |
| Amber | 75 |
| Emerald | 155 |
Custom presets
Section titled “Custom presets”import { HuePicker } from 'tailwind-hue-theme/widget'
new HuePicker({ presets: [ { label: 'Sky', hue: 220 }, { label: 'Pink', hue: 330 }, ],})Programmatic use (no floating UI)
Section titled “Programmatic use (no floating UI)”import { HuePicker } from 'tailwind-hue-theme/widget'
const picker = new HuePicker({ presets: [], // no swatches container: document.getElementById('my-container')!, onChange: (hue) => console.log('hue →', hue),})
// Drive the palette from your own UI:document.getElementById('my-slider')!.addEventListener('input', (e) => { picker.setHue(Number((e.target as HTMLInputElement).value))})Color scales
Section titled “Color scales”| Scale | Chroma range | Hue expression |
|---|---|---|
slate | 0.004–0.036 | var(--brand-h) |
indigo | 0.03–0.18 | var(--brand-h) |
cyan | 0.03–0.18 | calc(var(--brand-h) + <secondaryOffset>) |
The slate scale uses intentionally low chroma so that dark backgrounds (slate-900, slate-950) remain visually dark at any hue — they just gain a subtle tint.
TypeScript
Section titled “TypeScript”All exports are fully typed. The package ships .d.ts files alongside CJS and ESM bundles.
import { createHuePlugin, buildTokens, type HuePluginOptions } from 'tailwind-hue-theme'import { HuePicker, DEFAULT_PRESETS, getWidgetCSS, type HuePickerOptions, type HuePreset } from 'tailwind-hue-theme/widget'License
Section titled “License”MIT — see LICENSE.
Created by Peter Benoit