{"id":45679808,"url":"https://github.com/tenphi/glaze","last_synced_at":"2026-02-28T17:08:11.366Z","repository":{"id":339007781,"uuid":"1159849275","full_name":"tenphi/glaze","owner":"tenphi","description":"Color generation API for tasty themes.","archived":false,"fork":false,"pushed_at":"2026-02-17T16:16:55.000Z","size":154,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-17T19:34:08.976Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tenphi.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-17T08:35:35.000Z","updated_at":"2026-02-17T16:16:25.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/tenphi/glaze","commit_stats":null,"previous_names":["tenphi/glaze"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/tenphi/glaze","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tenphi%2Fglaze","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tenphi%2Fglaze/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tenphi%2Fglaze/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tenphi%2Fglaze/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tenphi","download_url":"https://codeload.github.com/tenphi/glaze/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tenphi%2Fglaze/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29785300,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-24T10:45:18.109Z","status":"ssl_error","status_checked_at":"2026-02-24T10:45:09.911Z","response_time":75,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-02-24T14:01:41.276Z","updated_at":"2026-02-24T14:01:41.915Z","avatar_url":"https://github.com/tenphi.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/glaze.svg\" width=\"128\" height=\"128\" alt=\"Glaze logo\"\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003eGlaze\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  OKHSL-based color theme generator with WCAG contrast solving\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.npmjs.com/package/@tenphi/glaze\"\u003e\u003cimg src=\"https://img.shields.io/npm/v/@tenphi/glaze.svg\" alt=\"npm version\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/tenphi/glaze/actions/workflows/ci.yml\"\u003e\u003cimg src=\"https://github.com/tenphi/glaze/actions/workflows/ci.yml/badge.svg\" alt=\"CI\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/tenphi/glaze/blob/main/LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/npm/l/@tenphi/glaze.svg\" alt=\"license\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\nGlaze generates robust **light**, **dark**, and **high-contrast** color schemes from a single hue/saturation seed. It preserves WCAG contrast ratios for UI color pairs via explicit dependency declarations — no hidden role math, no magic multipliers.\n\n## Features\n\n- **OKHSL color space** — perceptually uniform hue and saturation\n- **WCAG 2 contrast solving** — automatic lightness adjustment to meet AA/AAA targets\n- **Shadow colors** — OKHSL-native shadow computation with automatic alpha, fg/bg tinting, and per-scheme adaptation\n- **Light + Dark + High-Contrast** — all schemes from one definition\n- **Per-color hue override** — absolute or relative hue shifts within a theme\n- **Multi-format output** — `okhsl`, `rgb`, `hsl`, `oklch` with modern CSS space syntax\n- **CSS custom properties export** — ready-to-use `--var: value;` declarations per scheme\n- **Import/Export** — serialize and restore theme configurations\n- **Create from hex/RGB** — start from an existing brand color\n- **Zero dependencies** — pure math, runs anywhere (Node.js, browser, edge)\n- **Tree-shakeable ESM + CJS** — dual-format package\n- **TypeScript-first** — full type definitions included\n\n## Installation\n\n```bash\npnpm add @tenphi/glaze\n```\n\n```bash\nnpm install @tenphi/glaze\n```\n\n```bash\nyarn add @tenphi/glaze\n```\n\n## Quick Start\n\n```ts\nimport { glaze } from '@tenphi/glaze';\n\n// Create a theme from a hue (0–360) and saturation (0–100)\nconst primary = glaze(280, 80);\n\n// Define colors with explicit lightness and contrast relationships\nprimary.colors({\n  surface:       { lightness: 97, saturation: 0.75 },\n  text:          { base: 'surface', lightness: '-52', contrast: 'AAA' },\n  border:        { base: 'surface', lightness: ['-7', '-20'], contrast: 'AA-large' },\n  'accent-fill': { lightness: 52, mode: 'fixed' },\n  'accent-text': { base: 'accent-fill', lightness: '+48', contrast: 'AA', mode: 'fixed' },\n});\n\n// Create status themes by rotating the hue\nconst danger  = primary.extend({ hue: 23 });\nconst success = primary.extend({ hue: 157 });\n\n// Compose into a palette and export\nconst palette = glaze.palette({ primary, danger, success });\nconst tokens = palette.tokens({ prefix: true });\n// → { light: { 'primary-surface': 'okhsl(...)', ... }, dark: { 'primary-surface': 'okhsl(...)', ... } }\n```\n\n## Core Concepts\n\n### One Theme = One Hue Family\n\nA single `glaze` theme is tied to one hue/saturation seed. Status colors (danger, success, warning) are derived via `extend`, which inherits all color definitions and replaces the seed.\n\nIndividual colors can override the hue via the `hue` prop (see [Per-Color Hue Override](#per-color-hue-override)), but the primary purpose of a theme is to scope colors with the same hue.\n\n### Color Definitions\n\nEvery color is defined explicitly. No implicit roles — every value is stated.\n\n#### Root Colors (explicit position)\n\n```ts\nprimary.colors({\n  surface: { lightness: 97, saturation: 0.75 },\n  border:  { lightness: 90, saturation: 0.20 },\n});\n```\n\n- `lightness` — lightness in the light scheme (0–100)\n- `saturation` — saturation factor applied to the seed saturation (0–1, default: `1`)\n\n#### Dependent Colors (relative to base)\n\n```ts\nprimary.colors({\n  surface: { lightness: 97, saturation: 0.75 },\n  text:    { base: 'surface', lightness: '-52', contrast: 'AAA' },\n});\n```\n\n- `base` — name of another color in the same theme\n- `lightness` — position of this color (see [Lightness Values](#lightness-values))\n- `contrast` — ensures the WCAG contrast ratio meets a target floor against the base\n\n### Lightness Values\n\nThe `lightness` prop accepts two forms:\n\n| Form | Example | Meaning |\n|---|---|---|\n| Number (absolute) | `lightness: 45` | Absolute lightness 0–100 |\n| String (relative) | `lightness: '-52'` | Relative to base color's lightness |\n\n**Absolute lightness** on a dependent color (with `base`) positions the color independently. In dark mode, it is dark-mapped on its own. The `contrast` WCAG solver acts as a safety net.\n\n**Relative lightness** applies a signed delta to the base color's resolved lightness. In dark mode with `auto` adaptation, the sign flips automatically.\n\n```ts\n// Relative: 97 - 52 = 45 in light mode\n'text': { base: 'surface', lightness: '-52' }\n\n// Absolute: lightness 45 in light mode, dark-mapped independently\n'text': { base: 'surface', lightness: 45 }\n```\n\nA dependent color with `base` but no `lightness` inherits the base's lightness (equivalent to a delta of 0).\n\n### Per-Color Hue Override\n\nIndividual colors can override the theme's hue. The `hue` prop accepts:\n\n| Form | Example | Meaning |\n|---|---|---|\n| Number (absolute) | `hue: 120` | Absolute hue 0–360 |\n| String (relative) | `hue: '+20'` | Relative to the **theme seed** hue |\n\n**Important:** Relative hue is always relative to the **theme seed hue**, not to a base color's hue.\n\n```ts\nconst theme = glaze(280, 80);\ntheme.colors({\n  surface:     { lightness: 97 },\n  // Gradient end — slight hue shift from seed (280 + 20 = 300)\n  gradientEnd: { lightness: 90, hue: '+20' },\n  // Entirely different hue\n  warning:     { lightness: 60, hue: 40 },\n});\n```\n\n### contrast (WCAG Floor)\n\nEnsures the WCAG contrast ratio meets a target floor. Accepts a numeric ratio or a preset string:\n\n```ts\ntype MinContrast = number | 'AA' | 'AAA' | 'AA-large' | 'AAA-large';\n```\n\n| Preset | Ratio |\n|---|---|\n| `'AA'` | 4.5 |\n| `'AAA'` | 7 |\n| `'AA-large'` | 3 |\n| `'AAA-large'` | 4.5 |\n\nYou can also pass any numeric ratio directly (e.g., `contrast: 4.5`, `contrast: 7`, `contrast: 11`).\n\nThe constraint is applied independently for each scheme. If the `lightness` already satisfies the floor, it's kept. Otherwise, the solver adjusts lightness until the target is met.\n\n### High-Contrast via Array Values\n\n`lightness` and `contrast` accept a `[normal, high-contrast]` pair:\n\n```ts\n'border': { base: 'surface', lightness: ['-7', '-20'], contrast: 'AA-large' }\n//                                        ↑      ↑\n//                                    normal  high-contrast\n```\n\nA single value applies to both modes. All control is local and explicit.\n\n```ts\n'text':   { base: 'surface', lightness: '-52', contrast: 'AAA' }\n'border': { base: 'surface', lightness: ['-7', '-20'], contrast: 'AA-large' }\n'muted':  { base: 'surface', lightness: ['-35', '-50'], contrast: ['AA-large', 'AA'] }\n```\n\n## Theme Color Management\n\n### Adding Colors\n\n`.colors(defs)` performs an **additive merge** — it adds new colors and overwrites existing ones by name, but does not remove other colors:\n\n```ts\nconst theme = glaze(280, 80);\ntheme.colors({ surface: { lightness: 97 } });\ntheme.colors({ text: { lightness: 30 } });\n// Both 'surface' and 'text' are now defined\n```\n\n### Single Color Getter/Setter\n\n`.color(name)` returns the definition, `.color(name, def)` sets it:\n\n```ts\ntheme.color('surface', { lightness: 97, saturation: 0.75 }); // set\nconst def = theme.color('surface');                     // get → { lightness: 97, saturation: 0.75 }\n```\n\n### Removing Colors\n\n`.remove(name)` or `.remove([name1, name2])` deletes color definitions:\n\n```ts\ntheme.remove('surface');\ntheme.remove(['text', 'border']);\n```\n\n### Introspection\n\n```ts\ntheme.has('surface');  // → true/false\ntheme.list();          // → ['surface', 'text', 'border', ...]\n```\n\n### Clearing All Colors\n\n```ts\ntheme.reset(); // removes all color definitions\n```\n\n## Import / Export\n\nSerialize a theme's configuration (hue, saturation, color definitions) to a plain JSON-safe object, and restore it later:\n\n```ts\n// Export\nconst snapshot = theme.export();\n// → { hue: 280, saturation: 80, colors: { surface: { lightness: 97, saturation: 0.75 }, ... } }\n\nconst jsonString = JSON.stringify(snapshot);\n\n// Import\nconst restored = glaze.from(JSON.parse(jsonString));\n// restored is a fully functional GlazeTheme\n```\n\nThe export contains only the configuration — not resolved color values. Resolved values are recomputed on demand.\n\n## Standalone Color Token\n\nCreate a single color token without a full theme:\n\n```ts\nconst accent = glaze.color({ hue: 280, saturation: 80, lightness: 52, mode: 'fixed' });\n\naccent.resolve();  // → ResolvedColor with light/dark/lightContrast/darkContrast\naccent.token();    // → { '': 'okhsl(...)', '@dark': 'okhsl(...)' }  (tasty format)\naccent.tasty();    // → { '': 'okhsl(...)', '@dark': 'okhsl(...)' }  (same as token)\naccent.json();     // → { light: 'okhsl(...)', dark: 'okhsl(...)' }\n```\n\nStandalone colors are always root colors (no `base`/`contrast`).\n\n## From Existing Colors\n\nCreate a theme from an existing brand color by extracting its OKHSL hue and saturation:\n\n```ts\n// From hex\nconst brand = glaze.fromHex('#7a4dbf');\n\n// From RGB (0–255)\nconst brand = glaze.fromRgb(122, 77, 191);\n```\n\nThe resulting theme has the extracted hue and saturation. Add colors as usual:\n\n```ts\nbrand.colors({\n  surface: { lightness: 97, saturation: 0.75 },\n  text:    { base: 'surface', lightness: '-52', contrast: 'AAA' },\n});\n```\n\n## Shadow Colors\n\nShadow colors are colors with computed alpha. Instead of a parallel shadow system, they extend the existing color pipeline. All math is done natively in OKHSL.\n\n### Defining Shadow Colors\n\nShadow colors use `type: 'shadow'` and reference a `bg` (background) color and optionally an `fg` (foreground) color for tinting and intensity modulation:\n\n```ts\ntheme.colors({\n  surface: { lightness: 95 },\n  text:    { base: 'surface', lightness: '-52', contrast: 'AAA' },\n\n  'shadow-sm': { type: 'shadow', bg: 'surface', fg: 'text', intensity: 5 },\n  'shadow-md': { type: 'shadow', bg: 'surface', fg: 'text', intensity: 10 },\n  'shadow-lg': { type: 'shadow', bg: 'surface', fg: 'text', intensity: 20 },\n});\n```\n\nShadow colors are included in all output methods (`tokens()`, `tasty()`, `css()`, `json()`) alongside regular colors:\n\n```ts\ntheme.tokens({ format: 'oklch' });\n// light: { 'shadow-md': 'oklch(0.15 0.009 282 / 0.1)', ... }\n// dark:  { 'shadow-md': 'oklch(0.06 0.004 0 / 0.49)', ... }\n```\n\n### How Shadows Work\n\nThe shadow algorithm computes a dark, low-saturation pigment color and an alpha value that produces the desired visual intensity:\n\n1. **Contrast weight** — when `fg` is provided, shadow strength scales with `|l_bg - l_fg|`. Dark text on a light background produces a strong shadow; near-background-lightness elements produce barely visible shadows.\n2. **Pigment color** — hue blended between fg and bg, low saturation, dark lightness.\n3. **Alpha** — computed via a `tanh` curve that saturates smoothly toward `alphaMax` (default 0.6), ensuring well-separated shadow levels even on dark backgrounds.\n\n### Achromatic Shadows\n\nOmit `fg` for a pure achromatic shadow at full user-specified intensity:\n\n```ts\ntheme.colors({\n  'drop-shadow': { type: 'shadow', bg: 'surface', intensity: 12 },\n});\n```\n\n### High-Contrast Intensity\n\n`intensity` supports `[normal, highContrast]` pairs:\n\n```ts\ntheme.colors({\n  'shadow-card': { type: 'shadow', bg: 'surface', fg: 'text', intensity: [10, 20] },\n});\n```\n\n### Fixed Opacity (Regular Colors)\n\nFor a simple fixed-alpha color (no shadow algorithm), use `opacity` on a regular color:\n\n```ts\ntheme.colors({\n  overlay: { lightness: 0, opacity: 0.5 },\n});\n// → 'oklch(0 0 0 / 0.5)'\n```\n\n### Shadow Tuning\n\nFine-tune shadow behavior per-color or globally:\n\n```ts\n// Per-color tuning\ntheme.colors({\n  'shadow-soft': {\n    type: 'shadow', bg: 'surface', intensity: 10,\n    tuning: { alphaMax: 0.3, saturationFactor: 0.1 },\n  },\n});\n\n// Global tuning\nglaze.configure({\n  shadowTuning: { alphaMax: 0.5, bgHueBlend: 0.3 },\n});\n```\n\nAvailable tuning parameters:\n\n| Parameter | Default | Description |\n|---|---|---|\n| `saturationFactor` | 0.18 | Fraction of fg saturation kept in pigment |\n| `maxSaturation` | 0.25 | Upper clamp on pigment saturation |\n| `lightnessFactor` | 0.25 | Multiplier for bg lightness to pigment lightness |\n| `lightnessBounds` | [0.05, 0.20] | Clamp range for pigment lightness |\n| `minGapTarget` | 0.05 | Target minimum gap between pigment and bg lightness |\n| `alphaMax` | 0.6 | Asymptotic maximum alpha |\n| `bgHueBlend` | 0.2 | Blend weight pulling pigment hue toward bg hue |\n\n### Standalone Shadow Computation\n\nCompute a shadow outside of a theme:\n\n```ts\nconst v = glaze.shadow({\n  bg: '#f0eef5',\n  fg: '#1a1a2e',\n  intensity: 10,\n});\n// → { h: 280, s: 0.14, l: 0.2, alpha: 0.1 }\n\nconst css = glaze.format(v, 'oklch');\n// → 'oklch(0.15 0.014 280 / 0.1)'\n```\n\n### Consuming in CSS\n\n```css\n.card {\n  box-shadow: 0 2px 6px var(--shadow-sm-color),\n              0 8px 24px var(--shadow-md-color);\n}\n```\n\n## Output Formats\n\nControl the color format in exports with the `format` option:\n\n```ts\n// Default: OKHSL\ntheme.tokens();                        // → 'okhsl(280 60% 97%)'\n\n// RGB (modern space syntax, rounded integers)\ntheme.tokens({ format: 'rgb' });       // → 'rgb(244 240 250)'\n\n// HSL (modern space syntax)\ntheme.tokens({ format: 'hsl' });       // → 'hsl(270.5 45.2% 95.8%)'\n\n// OKLCH\ntheme.tokens({ format: 'oklch' });     // → 'oklch(0.965 0.0123 280)'\n```\n\nThe `format` option works on all export methods: `theme.tokens()`, `theme.tasty()`, `theme.json()`, `theme.css()`, `palette.tokens()`, `palette.tasty()`, `palette.json()`, `palette.css()`, and standalone `glaze.color().token()` / `.tasty()` / `.json()`.\n\nColors with `alpha \u003c 1` (shadow colors, or regular colors with `opacity`) include an alpha component:\n\n```ts\n// → 'oklch(0.15 0.009 282 / 0.1)'\n// → 'rgb(34 28 42 / 0.1)'\n```\n\nAvailable formats:\n\n| Format | Output (alpha = 1) | Output (alpha \u003c 1) | Notes |\n|---|---|---|---|\n| `'okhsl'` (default) | `okhsl(H S% L%)` | `okhsl(H S% L% / A)` | Native format, not a CSS function |\n| `'rgb'` | `rgb(R G B)` | `rgb(R G B / A)` | Rounded integers, space syntax |\n| `'hsl'` | `hsl(H S% L%)` | `hsl(H S% L% / A)` | Modern space syntax |\n| `'oklch'` | `oklch(L C H)` | `oklch(L C H / A)` | OKLab-based LCH |\n\nAll numeric output strips trailing zeros for cleaner CSS (e.g., `95` not `95.0`).\n\n## Adaptation Modes\n\nModes control how colors adapt across schemes:\n\n| Mode | Behavior |\n|---|---|\n| `'auto'` (default) | Full adaptation. Light ↔ dark inversion. High-contrast boost. |\n| `'fixed'` | Color stays recognizable. Only safety corrections. For brand buttons, CTAs. |\n| `'static'` | No adaptation. Same value in every scheme. |\n\n### How Relative Lightness Adapts\n\n**`auto` mode** — relative lightness sign flips in dark scheme:\n\n```ts\n// Light: surface L=97, text lightness='-52' → L=45 (dark text on light bg)\n// Dark:  surface inverts to L≈14, sign flips → L=14+52=66\n//        contrast solver may push further (light text on dark bg)\n```\n\n**`fixed` mode** — lightness is mapped (not inverted), relative sign preserved:\n\n```ts\n// Light: accent-fill L=52, accent-text lightness='+48' → L=100 (white on brand)\n// Dark:  accent-fill maps to L≈51.6, sign preserved → L≈99.6\n```\n\n**`static` mode** — no adaptation, same value in every scheme.\n\n## Dark Scheme Mapping\n\n### Lightness\n\n**`auto`** — inverted within the configured window:\n\n```ts\nconst [lo, hi] = darkLightness; // default: [10, 90]\nconst invertedL = ((100 - lightness) * (hi - lo)) / 100 + lo;\n```\n\n**`fixed`** — mapped without inversion:\n\n```ts\nconst mappedL = (lightness * (hi - lo)) / 100 + lo;\n```\n\n| Color | Light L | Auto (inverted) | Fixed (mapped) |\n|---|---|---|---|\n| surface (L=97) | 97 | 12.4 | 87.6 |\n| accent-fill (L=52) | 52 | 48.4 | 51.6 |\n| accent-text (L=100) | 100 | 10 | 90 |\n\n### Saturation\n\n`darkDesaturation` reduces saturation for all colors in dark scheme:\n\n```ts\nS_dark = S_light * (1 - darkDesaturation) // default: 0.1\n```\n\n## Inherited Themes (`extend`)\n\n`extend` creates a new theme inheriting all color definitions, replacing the hue and/or saturation seed:\n\n```ts\nconst primary = glaze(280, 80);\nprimary.colors({ /* ... */ });\n\nconst danger  = primary.extend({ hue: 23 });\nconst success = primary.extend({ hue: 157 });\nconst warning = primary.extend({ hue: 84 });\n```\n\nOverride individual colors (additive merge):\n\n```ts\nconst danger = primary.extend({\n  hue: 23,\n  colors: { 'accent-fill': { lightness: 48, mode: 'fixed' } },\n});\n```\n\n## Palette Composition\n\nCombine multiple themes into a single palette:\n\n```ts\nconst palette = glaze.palette({ primary, danger, success, warning });\n```\n\n### Token Export\n\nTokens are grouped by scheme variant, with plain color names as keys:\n\n```ts\nconst tokens = palette.tokens({ prefix: true });\n// → {\n//   light: { 'primary-surface': 'okhsl(...)', 'danger-surface': 'okhsl(...)' },\n//   dark:  { 'primary-surface': 'okhsl(...)', 'danger-surface': 'okhsl(...)' },\n// }\n```\n\nCustom prefix mapping:\n\n```ts\npalette.tokens({ prefix: { primary: 'brand-', danger: 'error-' } });\n```\n\n### Tasty Export (for [Tasty](https://cube-ui-kit.vercel.app/?path=/docs/tasty-documentation--docs) style system)\n\nThe `tasty()` method exports tokens in the [Tasty](https://cube-ui-kit.vercel.app/?path=/docs/tasty-documentation--docs) style-to-state binding format — `#name` color token keys with state aliases (`''`, `@dark`, etc.):\n\n```ts\nconst tastyTokens = palette.tasty({ prefix: true });\n// → {\n//   '#primary-surface': { '': 'okhsl(...)', '@dark': 'okhsl(...)' },\n//   '#danger-surface':  { '': 'okhsl(...)', '@dark': 'okhsl(...)' },\n// }\n```\n\nApply as global styles to make color tokens available app-wide:\n\n```ts\nimport { useGlobalStyles } from '@cube-dev/ui-kit';\n\n// In your root component\nuseGlobalStyles('body', tastyTokens);\n```\n\nFor zero-runtime builds, use `tastyStatic` to generate the CSS at build time:\n\n```ts\nimport { tastyStatic } from '@cube-dev/ui-kit';\n\ntastyStatic('body', tastyTokens);\n```\n\nAlternatively, register as a recipe via `configure()`:\n\n```ts\nimport { configure, tasty } from '@cube-dev/ui-kit';\n\nconfigure({\n  recipes: {\n    'all-themes': tastyTokens,\n  },\n});\n\nconst Page = tasty({\n  styles: {\n    recipe: 'all-themes',\n    fill: '#primary-surface',\n    color: '#primary-text',\n  },\n});\n```\n\nOr spread directly into component styles:\n\n```ts\nconst Card = tasty({\n  styles: {\n    ...tastyTokens,\n    fill: '#primary-surface',\n    color: '#primary-text',\n  },\n});\n```\n\nCustom prefix mapping:\n\n```ts\npalette.tasty({ prefix: { primary: 'brand-', danger: 'error-' } });\n```\n\nCustom state aliases:\n\n```ts\npalette.tasty({ states: { dark: '@dark', highContrast: '@hc' } });\n```\n\n### JSON Export (Framework-Agnostic)\n\n```ts\nconst data = palette.json({ prefix: true });\n// → {\n//   primary: { surface: { light: 'okhsl(...)', dark: 'okhsl(...)' } },\n//   danger:  { surface: { light: 'okhsl(...)', dark: 'okhsl(...)' } },\n// }\n```\n\n### CSS Export\n\nExport as CSS custom property declarations, grouped by scheme variant. Each variant is a string of `--name-color: value;` lines that you can wrap in your own selectors and media queries.\n\n```ts\nconst css = theme.css();\n// css.light        → \"--surface-color: rgb(...);\\n--text-color: rgb(...);\"\n// css.dark         → \"--surface-color: rgb(...);\\n--text-color: rgb(...);\"\n// css.lightContrast → \"--surface-color: rgb(...);\\n--text-color: rgb(...);\"\n// css.darkContrast  → \"--surface-color: rgb(...);\\n--text-color: rgb(...);\"\n```\n\nUse in a stylesheet:\n\n```ts\nconst css = palette.css({ prefix: true });\n\nconst stylesheet = `\n:root { ${css.light} }\n@media (prefers-color-scheme: dark) {\n  :root { ${css.dark} }\n}\n`;\n```\n\nOptions:\n\n| Option | Default | Description |\n|---|---|---|\n| `format` | `'rgb'` | Color format (`'rgb'`, `'hsl'`, `'okhsl'`, `'oklch'`) |\n| `suffix` | `'-color'` | Suffix appended to each CSS property name |\n| `prefix` | — | (palette only) Same prefix behavior as `tokens()` |\n\n```ts\n// Custom suffix\ntheme.css({ suffix: '' });\n// → \"--surface: rgb(...);\"\n\n// Custom format\ntheme.css({ format: 'hsl' });\n// → \"--surface-color: hsl(...);\"\n\n// Palette with prefix\npalette.css({ prefix: true });\n// → \"--primary-surface-color: rgb(...);\\n--danger-surface-color: rgb(...);\"\n```\n\n## Output Modes\n\nControl which scheme variants appear in exports:\n\n```ts\n// Light only\npalette.tokens({ modes: { dark: false, highContrast: false } });\n// → { light: { ... } }\n\n// Light + dark (default)\npalette.tokens({ modes: { highContrast: false } });\n// → { light: { ... }, dark: { ... } }\n\n// All four variants\npalette.tokens({ modes: { dark: true, highContrast: true } });\n// → { light: { ... }, dark: { ... }, lightContrast: { ... }, darkContrast: { ... } }\n```\n\nThe `modes` option works the same way on `tokens()`, `tasty()`, `json()`, and `css()`.\n\nResolution priority (highest first):\n\n1. `tokens({ modes })` / `tasty({ modes })` / `json({ modes })` / `css({ ... })` — per-call override\n2. `glaze.configure({ modes })` — global config\n3. Built-in default: `{ dark: true, highContrast: false }`\n\n## Configuration\n\n```ts\nglaze.configure({\n  darkLightness: [10, 90],    // Dark scheme lightness window [lo, hi]\n  darkDesaturation: 0.1,       // Saturation reduction in dark scheme (0–1)\n  states: {\n    dark: '@dark',             // State alias for dark mode tokens\n    highContrast: '@high-contrast',\n  },\n  modes: {\n    dark: true,                // Include dark variants in exports\n    highContrast: false,       // Include high-contrast variants\n  },\n  shadowTuning: {              // Default tuning for all shadow colors\n    alphaMax: 0.6,\n    bgHueBlend: 0.2,\n  },\n});\n```\n\n## Color Definition Shape\n\n`ColorDef` is a discriminated union of regular colors and shadow colors:\n\n```ts\ntype ColorDef = RegularColorDef | ShadowColorDef;\n\ninterface RegularColorDef {\n  lightness?: HCPair\u003cnumber | RelativeValue\u003e;\n  saturation?: number;\n  hue?: number | RelativeValue;\n  base?: string;\n  contrast?: HCPair\u003cMinContrast\u003e;\n  mode?: 'auto' | 'fixed' | 'static';\n  opacity?: number; // fixed alpha (0–1)\n}\n\ninterface ShadowColorDef {\n  type: 'shadow';\n  bg: string;       // background color name (non-shadow)\n  fg?: string;      // foreground color name (non-shadow)\n  intensity: HCPair\u003cnumber\u003e; // 0–100\n  tuning?: ShadowTuning;\n}\n```\n\nA root color must have absolute `lightness` (a number). A dependent color must have `base`. Relative `lightness` (a string) requires `base`. Shadow colors use `type: 'shadow'` and must reference a non-shadow `bg` color.\n\n## Validation\n\n| Condition | Behavior |\n|---|---|\n| Both absolute `lightness` and `base` on same color | Warning, `lightness` takes precedence |\n| `contrast` without `base` | Validation error |\n| Relative `lightness` without `base` | Validation error |\n| `lightness` resolves outside 0–100 | Clamp silently |\n| `saturation` outside 0–1 | Clamp silently |\n| Circular `base` references | Validation error |\n| `base` references non-existent name | Validation error |\n| Shadow `bg` references non-existent color | Validation error |\n| Shadow `fg` references non-existent color | Validation error |\n| Shadow `bg` references another shadow color | Validation error |\n| Shadow `fg` references another shadow color | Validation error |\n| Regular color `base` references a shadow color | Validation error |\n| Shadow `intensity` outside 0–100 | Clamp silently |\n| `contrast` + `opacity` combined | Warning |\n\n## Advanced: Color Math Utilities\n\nGlaze re-exports its internal color math for advanced use:\n\n```ts\nimport {\n  okhslToLinearSrgb,\n  okhslToSrgb,\n  okhslToOklab,\n  srgbToOkhsl,\n  parseHex,\n  relativeLuminanceFromLinearRgb,\n  contrastRatioFromLuminance,\n  formatOkhsl,\n  formatRgb,\n  formatHsl,\n  formatOklch,\n  findLightnessForContrast,\n  resolveMinContrast,\n} from '@tenphi/glaze';\n```\n\n## Full Example\n\n```ts\nimport { glaze } from '@tenphi/glaze';\n\nconst primary = glaze(280, 80);\n\nprimary.colors({\n  surface:       { lightness: 97, saturation: 0.75 },\n  text:          { base: 'surface', lightness: '-52', contrast: 'AAA' },\n  border:        { base: 'surface', lightness: ['-7', '-20'], contrast: 'AA-large' },\n  bg:            { lightness: 97, saturation: 0.75 },\n  icon:          { lightness: 60, saturation: 0.94 },\n  'accent-fill': { lightness: 52, mode: 'fixed' },\n  'accent-text': { base: 'accent-fill', lightness: '+48', contrast: 'AA', mode: 'fixed' },\n  disabled:      { lightness: 81, saturation: 0.4 },\n\n  // Shadow colors — computed alpha, automatic dark-mode adaptation\n  'shadow-sm':   { type: 'shadow', bg: 'surface', fg: 'text', intensity: 5 },\n  'shadow-md':   { type: 'shadow', bg: 'surface', fg: 'text', intensity: 10 },\n  'shadow-lg':   { type: 'shadow', bg: 'surface', fg: 'text', intensity: 20 },\n\n  // Fixed-alpha overlay\n  overlay:       { lightness: 0, opacity: 0.5 },\n});\n\nconst danger  = primary.extend({ hue: 23 });\nconst success = primary.extend({ hue: 157 });\nconst warning = primary.extend({ hue: 84 });\nconst note    = primary.extend({ hue: 302 });\n\nconst palette = glaze.palette({ primary, danger, success, warning, note });\n\n// Export as flat token map grouped by variant\nconst tokens = palette.tokens({ prefix: true });\n// tokens.light → { 'primary-surface': 'okhsl(...)', 'primary-shadow-md': 'okhsl(... / 0.1)' }\n\n// Export as tasty style-to-state bindings (for Tasty style system)\nconst tastyTokens = palette.tasty({ prefix: true });\n\n// Export as CSS custom properties (rgb format by default)\nconst css = palette.css({ prefix: true });\n// css.light → \"--primary-surface-color: rgb(...);\\n--primary-shadow-md-color: rgb(... / 0.1);\"\n\n// Standalone shadow computation\nconst v = glaze.shadow({ bg: '#f0eef5', fg: '#1a1a2e', intensity: 10 });\nconst shadowCss = glaze.format(v, 'oklch');\n// → 'oklch(0.15 0.014 280 / 0.1)'\n\n// Save and restore a theme\nconst snapshot = primary.export();\nconst restored = glaze.from(snapshot);\n\n// Create from an existing brand color\nconst brand = glaze.fromHex('#7a4dbf');\nbrand.colors({ surface: { lightness: 97 }, text: { base: 'surface', lightness: '-52' } });\n```\n\n## API Reference\n\n### Theme Creation\n\n| Method | Description |\n|---|---|\n| `glaze(hue, saturation?)` | Create a theme from hue (0–360) and saturation (0–100) |\n| `glaze({ hue, saturation })` | Create a theme from an options object |\n| `glaze.from(data)` | Create a theme from an exported configuration |\n| `glaze.fromHex(hex)` | Create a theme from a hex color (`#rgb` or `#rrggbb`) |\n| `glaze.fromRgb(r, g, b)` | Create a theme from RGB values (0–255) |\n| `glaze.color(input)` | Create a standalone color token |\n| `glaze.shadow(input)` | Compute a standalone shadow color (returns `ResolvedColorVariant`) |\n| `glaze.format(variant, format?)` | Format any `ResolvedColorVariant` as a CSS string |\n\n### Theme Methods\n\n| Method | Description |\n|---|---|\n| `theme.colors(defs)` | Add/replace colors (additive merge) |\n| `theme.color(name)` | Get a color definition |\n| `theme.color(name, def)` | Set a single color definition |\n| `theme.remove(names)` | Remove one or more colors |\n| `theme.has(name)` | Check if a color is defined |\n| `theme.list()` | List all defined color names |\n| `theme.reset()` | Clear all color definitions |\n| `theme.export()` | Export configuration as JSON-safe object |\n| `theme.extend(options)` | Create a child theme |\n| `theme.resolve()` | Resolve all colors |\n| `theme.tokens(options?)` | Export as flat token map grouped by variant |\n| `theme.tasty(options?)` | Export as [Tasty](https://cube-ui-kit.vercel.app/?path=/docs/tasty-documentation--docs) style-to-state bindings |\n| `theme.json(options?)` | Export as plain JSON |\n| `theme.css(options?)` | Export as CSS custom property declarations |\n\n### Global Configuration\n\n| Method | Description |\n|---|---|\n| `glaze.configure(config)` | Set global configuration |\n| `glaze.palette(themes)` | Compose themes into a palette |\n| `glaze.getConfig()` | Get current global config |\n| `glaze.resetConfig()` | Reset to defaults |\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftenphi%2Fglaze","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftenphi%2Fglaze","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftenphi%2Fglaze/lists"}