OKHST-based color theme generator with WCAG and APCA contrast solving
Glaze generates light, dark, and high-contrast color schemes from a single hue/saturation seed. Relationships stay explicit: colors either define an absolute tone or depend on a named base through a tone delta and an optional contrast floor.
- OKHST color space — OKHSL with a contrast-shaped tone axis. Equal steps give equal WCAG contrast for neutrals and a useful approximation for chromatic colors. See Tone and OKHST and the full OKHST specification.
- WCAG 2 + APCA contrast solving — automatic tone adjustment to meet a WCAG ratio or APCA Lc floor
- Unified dark mode — one tone space for light, dark, and high-contrast; dark is a single
100 − tinversion, no fitted curve - Mix colors — blend two colors with OKHSL or sRGB interpolation, opaque or transparent, with optional contrast solving
- Shadows and glows — translucent overlays whose opacity is solved per surface, so
intensityis an exact tone step wherever the surface has room for it (a step past black or white is pinned and warns), with object-aware strength - HDR glows — set
glowTuning.headroomand a glow whose step passes white also gets an exact HDR value: extended sRGBcolor(srgb …)with channels past 1, plus a base and an exposure in DTCG for native apps, exported when you turn onmodes.hdr. See HDR glows. - Light + Dark + High-Contrast — all schemes from one definition
- Manual contrast level — put your normal palette on a
0–100slider between itself and the high-contrast one; every level in between is solved, not approximated, and the high-contrast tier stays available on top - Per-color hue override — absolute or relative hue shifts within a theme
- Multi-format output — native
rgb,hsl, andoklch, plus Tasty-compatibleokhslandokhst - CSS custom properties export — ready-to-use
--var: value;declarations per scheme - W3C DTCG export — spec-conformant
.tokens.json(2025.10) for Figma, Tokens Studio, Style Dictionary, and every DTCG tool - W3C DTCG Resolver-Module export — opt-in single-document
dtcgResolver()(sets + aschememodifier with a context per variant) for resolver tools such as Dispersa - Tailwind CSS v4 export —
@themeblock + dark / high-contrast overrides - Import/Export — serialize and restore themes, color tokens, and entire palettes as JSON
- Create from hex/RGB — start from an existing brand color
- Zero dependencies — pure math, runs anywhere (Node.js, browser, edge)
- ESM + CJS — dual-format package
- TypeScript-first — full type definitions included
pnpm add @tenphi/glazenpm install @tenphi/glazeyarn add @tenphi/glazeimport { glaze } from '@tenphi/glaze';
glaze.configure({
modes: { dark: true, highContrast: true },
});
const theme = glaze(280, 80);
theme.colors({
surface: { tone: 97, saturation: 0.15 },
'surface-text': {
base: 'surface',
tone: '-1',
saturation: 0.4,
contrast: { apca: ['content', 'body'] },
},
border: {
base: 'surface',
tone: ['-8', '-16'],
saturation: 0.2,
inherit: false,
},
'accent-surface-text': { tone: 100, mode: 'fixed' },
'accent-surface': {
base: 'accent-surface-text',
tone: '-1',
contrast: 'AA',
mode: 'fixed',
},
});
const danger = theme.extend({ hue: 23 });
const palette = glaze.palette({ default: theme, danger });
const tokens = palette.tokens({
prefix: { default: '', danger: 'danger-' },
});
// → {
// light: { surface: 'oklch(...)', 'danger-surface': 'oklch(...)', ... },
// dark: { ... },
// lightContrast: { ... },
// darkContrast: { ... },
// }The accent fill is solved against its white label, not the reverse: the fill
darkens until the pair meets 'AA', or 'AAA' in high contrast, where a bare
'AA' rises a tier.
- A theme has one hue/saturation seed. Create status themes with
extend()so they inherit the same relationships while changing hue. - A color is a root or a dependent. Roots use an absolute
toneor'max'/'min'. Dependent colors name abaseand may use a signed tone delta such as'-8'plus acontrastfloor. - Tone is an authoring axis, not a contrast guarantee. Equal tone deltas are exact WCAG steps for neutrals. Chromatic colors can drift in rendered luminance, so Glaze measures contrast floors against the resolved colors.
contrastis a floor. A bare number or preset means WCAG;{ wcag }and{ apca }select the metric explicitly. The solver keeps the requested tone whenever it already clears the floor with a small margin (1% over a WCAG ratio, 0.5 Lc over an APCA target).- Each color chooses its dark adaptation.
mode: 'auto'uses dark tone inversion (100 - t),fixedkeeps the authored side of the tone scale, andstaticskips scheme adaptation. - Tone windows bound ordinary schemes.
lightToneanddarkToneconfigure the light and dark tone windows. High-contrast variants use the full range. Values that accept[normal, highContrast]pairs can tighten deliberately in HC.
In Glaze, a scheme variant is one of light, dark, lightContrast, or
darkContrast. Export modes choose which variants are included; Tasty
states choose how those variants are activated in an application.
tasty()returns Tasty#tokenbindings, including its customokhsl()orokhst()serialization.tokens()andjson()return JavaScript data and default to nativeoklch()values.css()returns custom-property declarations.dtcg()/dtcgResolver()target design-token tooling.tailwind()emits a Tailwind CSS v4 theme.
The full documentation, with live examples that follow your light / dark and
contrast preferences, is at glaze.tenphi.me. The
same pages live in docs/:
- Getting started — install, build a first theme, and export it.
- Concepts: light, dark, and high contrast, contrast solving, shadows and glows, mixes and transparency, and the color model under them, tone and OKHST.
- Guides: designing a palette, exporting and integration, and migration and upgrading (including Glaze 2 → 3).
- The API reference for every method, option, and type.
- Explore the tone axis in the ramp explorer, or read the full OKHST specification for its math.
AGENTS.md— source-tree orientation for contributors.