@tenphi/glaze 0.0.0-snapshot.fdc17e3 → 0.0.0-snapshot.fe2bc04

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md ADDED
@@ -0,0 +1,132 @@
1
+ # AGENTS.md — @tenphi/glaze
2
+
3
+ ## Package Overview
4
+
5
+ - **Package:** `@tenphi/glaze`
6
+ - **Repository:** [https://github.com/tenphi/glaze](https://github.com/tenphi/glaze)
7
+ - **Description:** Zero-dependency color theme generator built on OKHST (OKHSL with a contrast-uniform **tone** axis), with WCAG + APCA contrast solving for light, dark, and high-contrast schemes.
8
+ - **Runtime:** Node >= 20, browsers, edge. Pure math, no I/O.
9
+ - **Output formats:** ESM + CJS, with full `.d.ts`.
10
+
11
+ ## Project Structure
12
+
13
+ ```
14
+ glaze/
15
+ ├── src/ Source
16
+ ├── docs/ Long-form docs (okhst / api / methodology / migration)
17
+ ├── assets/ Logo + brand assets
18
+ ├── scripts/ Repo helpers
19
+ ├── README.md Compact intro
20
+ ├── AGENTS.md This file
21
+ ├── CHANGELOG.md Auto-generated by Changesets
22
+ ├── package.json
23
+ ├── tsconfig.json
24
+ ├── tsdown.config.ts Build config
25
+ ├── vitest.config.ts Test config
26
+ └── eslint.config.js
27
+ ```
28
+
29
+ ### `src/` files
30
+
31
+ | File | Description |
32
+ |---|---|
33
+ | [src/index.ts](src/index.ts) | Public barrel; re-exports `glaze`, every public type, the contrast-solver utilities, and the OKHSL/sRGB/OKLab math helpers. |
34
+ | [src/glaze.ts](src/glaze.ts) | `glaze()` factory + attached statics: `palette`, `paletteFrom`, `color`, `colorFrom`, `themeFrom`, `from` (alias), `fromHex`, `fromRgb`, `shadow`, `format`, `configure`, `getConfig`, `resetConfig`, `isThemeExport` / `isColorTokenExport` / `isPaletteExport`. Thin wiring layer over the focused modules below. |
35
+ | [src/theme.ts](src/theme.ts) | Single-theme factory (`createTheme`). Owns the mutable `ColorMap`, the `resolve()` cache (versioned against `getConfigVersion()`), and the `tokens` / `tasty` / `json` / `css` / `extend` / `export` methods. `export(override?)` deep-clones defs and freezes effective config with `kind`/`version`. |
36
+ | [src/palette.ts](src/palette.ts) | Multi-theme composition (`createPalette` / `createPaletteFromExport`). Shared per-theme driver `buildPaletteOutput` handles prefix resolution, primary duplication, collision filtering. Authoring: `export(override?)`, `theme` / `themes` / `list` / `primary`. |
37
+ | [src/color-token.ts](src/color-token.ts) | Standalone `glaze.color()` tokens. Parses values through `color-value.ts`; owns the structured-input validator, the two factory paths, sparse local config + live resolve, and the JSON-safe export / `glaze.colorFrom` rehydrate round-trip. |
38
+ | [src/color-value.ts](src/color-value.ts) | `GlazeColorValue` parsing + validation: hex (3/6/8), the `rgb()` / `hsl()` / `okhsl()` / `okhst()` / `oklch()` functions, and the four value-object shapes, all normalized to OKHSL by `extractOkhslFromValue`. Split out of `color-token.ts` so the resolver can reach it for a theme color's `from` without closing an import cycle. Leaf module — depends only on the color math. |
39
+ | [src/serialize.ts](src/serialize.ts) | Authoring-export helpers: `GLAZE_EXPORT_VERSION`, `assertExportKind` / `assertExportVersion`, and `isThemeExport` / `isColorTokenExport` / `isPaletteExport` type guards. |
40
+ | [src/resolver.ts](src/resolver.ts) | Four-pass solver (light → light-HC → dark → dark-HC), dropping to two-pass (light → dark) with mirrored HC slots only at `contrastLevel: 100`, where the normal passes already are the HC ones (`hcMirrorsNormal`). Under any other manual level `passTone` / `passNumber` / `resolveContrastSpec` feed interpolated inputs into the two *normal* passes and a probe solve at the nearer endpoint pins which side of its base a contrast-solved color sits on; the HC passes bypass the level and the probe entirely, so the HC tier is bit-identical to `'auto'`. Stores canonical tone (`t`) in variants; per-scheme branches for regular, shadow, and mix defs; integrates the contrast solver, and the OKHST tone helpers. `resolveChannels` owns per-scheme hue/saturation (incl. the `darkHue` / `darkSaturation` seed + def overrides, the `darkDesaturation` bypass, and the absolute hue/saturation a `from` color carries in place of the seed factor) and feeds both the emitted variant and the contrast solver. `configForColor` gives a `from` color a local `lightTone: false` so its light variant reproduces the authored value. Converts to/from OKHSL lightness only at the mix/shadow edges. Pre-seeds externally-resolved bases for `glaze.color({ base })`. |
41
+ | [src/okhst.ts](src/okhst.ts) | The OKHST tone layer. `REF_EPS`, tone↔lightness transfers (`toTone`/`fromTone`, `toneFromY`/`yFromTone`), OKHST↔OKHSL conversions, `variantToOkhsl` (tone→lightness at render), `normalizeToneWindow` (`[lo,hi]` / `{lo,hi,eps}` / `false` → `{lo,hi,eps}`), `mapToneForScheme` (scheme inversion + window remap, HC bypass), `mapSaturationDark` (the `darkDesaturation` reducer the resolver skips when a dark saturation is authored), and `schemeToneRange` for the solver. `activeWindow` owns the HC window bypass and its continuous form under `contrastLevel`. Only tone adapts here — hue/saturation are the resolver's business. |
42
+ | [src/contrast-solver.ts](src/contrast-solver.ts) | Tone-based binary-search solver for WCAG **and** APCA. Public API: `findToneForContrast` (incl. the `preferInitial` tie-break the manual level uses on its normal passes to keep a color's side stable), `findValueForMixContrast`, `resolveContrastForMode`, `resolveContrastForLevel` (both ends resolved, target interpolated for a manual `contrastLevel`; throws on a metric switch), `contrastMetricOf`, `resolveMinContrast`, `apcaContrast`. Closed-form WCAG seed + tone search. |
43
+ | [src/shadow.ts](src/shadow.ts) | Shadow + mix def predicates (`isShadowDef`, `isMixDef`), default `ShadowTuning`, tuning merge, the actual `computeShadow` math (hue blend, saturation cap, lightness clamp, `tanh` alpha curve) operating on OKHSL lightness at the edge, and `circularLerp` for hue. |
44
+ | [src/hc-pair.ts](src/hc-pair.ts) | Tiny shared helpers: HC-pair selection (`pairNormal` / `pairHC`), HC-pair interpolation for the manual contrast level (`contrastFraction`, `hcMirrorsNormal` — the level-100 "HC slots are duplicates" predicate, `levelFraction`, `numberAt`, `parseToneValueAt`, `PAIR_SWITCH`), tone-value parsing (`parseToneValue` for absolute / relative / `'max'`/`'min'` extremes, `isExtremeTone`), root-tone discrimination (`isAbsoluteTone`, now incl. extremes), `clamp` / `lerp`, hue resolution (`resolveEffectiveHue`), relative-value parsing. Leaf module — types only, so every other module can import it. |
45
+ | [src/formatters.ts](src/formatters.ts) | Variant→string dispatch (`okhsl` / `rgb` / `hsl` / `oklch`) and the four token-map shapes Glaze emits: `buildTokenMap` ([Tasty](https://tasty.style) `#name` keys + state aliases), `buildFlatTokenMap` (per-variant maps), `buildJsonMap` (per-color JSON), `buildCssMap` (CSS custom-property declarations). For `splitHue` it emits the light hue declarations plus, via `darkHueDeclarations` in `channels.ts`, the whole dark set when a `darkHue` makes it differ. Also the `resolveModes` helper used everywhere. |
46
+ | [src/okhsl-color-math.ts](src/okhsl-color-math.ts) | OKHSL ↔ linear-sRGB ↔ gamma-sRGB ↔ OKLab conversions, hex parsing (3/6/8 digits), gamut clamping, and the `formatOkhsl` / `formatOkhst` / `formatRgb` / `formatHsl` / `formatOklch` writers. Converters and writers share one scale — `h` 0–360, `s` / `l` / `t` 0–1 — so any converter composes with any writer (the 0–100 tone axis lives in `okhst.ts`'s `toTone` / `fromTone`, not here); `warnPercentScale` catches pre-2.0 percentage-scale input instead of emitting a plausible wrong color. The only file with the actual color science. |
47
+ | [src/config.ts](src/config.ts) | Global config singleton. `defaultConfig()` is the one source of truth for defaults; `configure()` mutates the live object and bumps a monotonic `configVersion` so theme caches invalidate. `snapshotConfig()` powers `glaze.getConfig()`. `freezeConfigForExport()` freezes resolve-relevant fields for authoring export (`pastel` is instance-only; `contrastLevel` is frozen only when instance-authored, since a global level is a live preference). `normalizeContrastLevel()` validates the manual contrast level. |
48
+ | [src/validation.ts](src/validation.ts) | `validateColorDefs` (missing references, shadow-bg-cannot-be-shadow, mix-cannot-target-shadow, contrast-without-base, relative-without-base, WCAG-vs-APCA metric switch in a `contrast` pair, etc.) + `topoSort` so the resolver processes each color after its dependencies. |
49
+ | [src/warnings.ts](src/warnings.ts) | Deduped contrast-unmet warnings. Caps cache at 256 entries to keep dev-server output bounded. |
50
+ | [src/types.ts](src/types.ts) | All public TypeScript types: `HCPair`, `MinContrast`, `ContrastSpec`, `RelativeValue`, `ExtremeValue` (`'max'`/`'min'`), `ToneValue`, `AdaptationMode`, `OkhstColor`, `ToneWindow` (`[lo,hi]` \| `{lo,hi,eps}` \| `false`), `ColorDef` discriminated union (with `tone` + `flip` + `darkHue` / `darkSaturation`), `GlazeConfig` (with `lightTone` / `darkTone` / `autoFlip` / `contrastLevel`), `GlazeThemeSeed` (`hue` / `saturation` + optional dark pair), `GlazeTheme`, `GlazePalette`, `GlazeColorToken`, the `*Export` shapes, etc. `ResolvedColorVariant` stores `{ h, s, t, alpha }`. |
51
+ | `src/glaze.test.ts` | Main test suite — covers the factory surface, resolver behavior, palette composition, shadow/mix algorithms, the manual contrast level, and the standalone color token round-trip. |
52
+ | `src/contrast-solver.test.ts` | Tests for the binary-search solver in isolation. |
53
+ | `src/hc-pair.test.ts` | Tests for the pure HC-pair selection and interpolation helpers. |
54
+ | `src/okhsl-color-math.test.ts` | Tests for the format writers' 0–1 scale contract, the percentage-scale guard, and producer→writer composition. |
55
+
56
+ ### `docs/` files
57
+
58
+ | File | Description |
59
+ |---|---|
60
+ | [docs/okhst.md](docs/okhst.md) | The OKHST color model spec — tone math, `REF_EPS`, the T↔L invariant, the scheme pipeline, calibrated window defaults, the contrast metric, and the APCA verification step. Source of truth for the color model. |
61
+ | [docs/api.md](docs/api.md) | Full API reference. Every method, every option, every type. Reads as a lookup, not a tutorial. |
62
+ | [docs/methodology.md](docs/methodology.md) | Palette design methodology — how to build a palette from scratch. Covers naming conventions, surface ladders, the accent anchor pattern, contrast-driven disabled chips, shadows, and `extend` composition. |
63
+ | [docs/migration.md](docs/migration.md) | Migration & integration guide — choosing an export shape, wiring tokens into [Tasty](https://tasty.style) / CSS / JSON, prefix-map strategies, migrating from a legacy color system, and common pitfalls. |
64
+
65
+ ## Commands
66
+
67
+ | Command | Description |
68
+ |---|---|
69
+ | `pnpm build` | Build ESM + CJS via tsdown (with `.d.ts` and sourcemaps). |
70
+ | `pnpm test` | Run all tests (Vitest 4). |
71
+ | `pnpm test -- <pattern>` | Run a subset of tests. |
72
+ | `pnpm test:watch` | Tests in watch mode. |
73
+ | `pnpm test:coverage` | Tests with coverage report. |
74
+ | `pnpm typecheck` | Type-check without emitting. |
75
+ | `pnpm lint` | ESLint over `src/`. |
76
+ | `pnpm lint:fix` | ESLint with `--fix`. |
77
+ | `pnpm format` | Prettier write `src/**/*.ts`. |
78
+ | `pnpm format:check` | Prettier check (no writes). |
79
+ | `pnpm changeset` | Create a changeset entry. |
80
+ | `pnpm release` | Build and publish (used by CI). |
81
+
82
+ ## Stack
83
+
84
+ - **Language:** TypeScript (strict mode, `consistent-type-imports` enforced)
85
+ - **Build:** [tsdown](https://github.com/rolldown/tsdown) — ESM + CJS, unbundled, dts + sourcemaps
86
+ - **Test:** [Vitest 4](https://vitest.dev/) with globals enabled
87
+ - **Lint:** ESLint 10 + typescript-eslint + `eslint-config-prettier`
88
+ - **Format:** Prettier — single quotes, semicolons, trailing commas, 80 cols
89
+ - **Versioning:** [Changesets](https://github.com/changesets/changesets)
90
+ - **Package manager:** pnpm 11
91
+
92
+ ## Code Conventions
93
+
94
+ - TypeScript strict mode; `consistent-type-imports` enforced.
95
+ - Test files use `*.test.ts` and live next to the module they test in `src/`.
96
+ - Unused vars prefixed with `_` are allowed.
97
+ - **No runtime dependencies** — every bit of math is implemented from scratch.
98
+ - Functional API pattern: `glaze()` factory with attached static methods; no classes.
99
+ - Each module starts with a JSDoc block describing its responsibility — keep these in sync when behavior changes.
100
+
101
+ ## Adding a New Color Type
102
+
103
+ If you need to add a fourth `ColorDef` variant (alongside regular / shadow / mix), touch these files in order:
104
+
105
+ 1. [src/types.ts](src/types.ts) — add the new interface and extend the `ColorDef` discriminated union.
106
+ 2. [src/shadow.ts](src/shadow.ts) — add a discriminator predicate (`isFooDef`).
107
+ 3. [src/validation.ts](src/validation.ts) — extend `validateColorDefs` (missing-reference / cycle handling) and `topoSort` (record the new dependency edges).
108
+ 4. [src/resolver.ts](src/resolver.ts) — add a per-scheme `resolveFooForScheme` branch and dispatch from `resolveColorForScheme`.
109
+ 5. [src/formatters.ts](src/formatters.ts) — only if the output shape differs (it usually doesn't; the resolver returns a normal `ResolvedColorVariant`).
110
+ 6. `src/glaze.test.ts` — add coverage for the new def, including HC pairs, dark-mode behavior, and inheritance through `extend`.
111
+
112
+ For a new public method on `glaze` (e.g. another statics entry), the wiring lives entirely in [src/glaze.ts](src/glaze.ts); the implementation should still go in a focused module.
113
+
114
+ ## CI/CD & Release
115
+
116
+ - **CI:** Runs lint, format check, typecheck, build, and tests on push to `main` and PRs.
117
+ - **Release:** On push to `main`, `changesets/action` either creates a version PR or publishes to npm.
118
+ - **Snapshot:** Comment `/snapshot` on a PR to publish a `0.0.0-snapshot.<sha>` release.
119
+ - **npm trusted publishing:** Uses OIDC provenance via the `release` GitHub environment; the workflow requires `environment: release` for npm to accept the OIDC token.
120
+ - **`RELEASE_TOKEN` secret:** Fine-grained GitHub PAT (repo-scoped, `contents: write` + `pull-requests: write`) used by `changesets/action` for Git pushes and PR creation. Rotate before expiry.
121
+ - **Repo settings required:** Settings → Actions → General → Workflow permissions → "Allow GitHub Actions to create and approve pull requests" must be enabled.
122
+
123
+ ## Before Pushing Changes
124
+
125
+ Follow [`.cursor/commands/submit-changes.md`](.cursor/commands/submit-changes.md) for the full release-oriented workflow. At minimum:
126
+
127
+ 1. **Typecheck** — `pnpm typecheck`. Stop and fix errors before continuing.
128
+ 2. **Lint** — `pnpm lint`. Stop and fix errors before continuing.
129
+ 3. **Format** — `pnpm format` so the diff matches Prettier output.
130
+ 4. **Changeset** — for any user-visible change (features, fixes, refactors, perf, docs that affect published behavior), add a markdown file in `.changeset/` with a `patch` / `minor` / `major` bump and a concise user-facing summary. Skip only for purely internal changes (CI, repo churn, tests with no behavior change).
131
+ 5. **Commit** — Conventional Commits (`feat`, `fix`, `refactor`, `test`, `docs`, `chore`, `perf`, `ci`). Optional scope. Keep the subject short. Include the changeset file in the same commit.
132
+ 6. **Push** — never push to `main`. Confirm the current branch, then `git push -u origin HEAD`.
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  <h1 align="center">Glaze</h1>
6
6
 
7
7
  <p align="center">
8
- OKHST-based color theme generator with WCAG contrast solving
8
+ OKHST-based color theme generator with WCAG and APCA contrast solving
9
9
  </p>
10
10
 
11
11
  <p align="center">
@@ -16,23 +16,31 @@
16
16
 
17
17
  ---
18
18
 
19
- Glaze generates robust **light**, **dark**, and **high-contrast** color schemes from a single hue/saturation seed. WCAG contrast is preserved via explicit dependency declarations — no hidden role math, no magic multipliers.
19
+ Glaze generates **light**, **dark**, and **high-contrast** color schemes from a
20
+ single hue/saturation seed. Relationships stay explicit: colors either define
21
+ an absolute tone or depend on a named base through a tone delta and an optional
22
+ contrast floor.
20
23
 
21
24
  ## Features
22
25
 
23
- - **OKHST color space** — OKHSL with a contrast-uniform **tone** axis (equal tone steps give equal contrast). See [`docs/okhst.md`](docs/okhst.md).
26
+ - **OKHST color space** — OKHSL with a contrast-shaped **tone** axis. Equal
27
+ steps give equal WCAG contrast for neutrals and a useful approximation for
28
+ chromatic colors. See [OKHST in Glaze](docs/okhst.md) and the
29
+ [full OKHST specification](https://github.com/tenphi/okhst).
24
30
  - **WCAG 2 + APCA contrast solving** — automatic tone adjustment to meet a WCAG ratio or APCA Lc floor
25
31
  - **Unified dark mode** — one tone space for light, dark, and high-contrast; dark is a single `100 − t` inversion, no fitted curve
26
32
  - **Mix colors** — blend two colors with OKHSL or sRGB interpolation, opaque or transparent, with optional contrast solving
27
33
  - **Shadow colors** — OKHSL-native shadow computation with automatic alpha, fg/bg tinting, and per-scheme adaptation
28
34
  - **Light + Dark + High-Contrast** — all schemes from one definition
35
+ - **Manual contrast level** — put your normal palette on a `0–100` slider between itself and the high-contrast one; every level in between is solved, not approximated, and the high-contrast tier stays available on top
29
36
  - **Per-color hue override** — absolute or relative hue shifts within a theme
30
- - **Multi-format output** — `okhsl`, `rgb`, `hsl`, `oklch` with modern CSS space syntax
37
+ - **Multi-format output** — native `rgb`, `hsl`, and `oklch`, plus
38
+ [Tasty](https://tasty.style)-compatible `okhsl` and `okhst`
31
39
  - **CSS custom properties export** — ready-to-use `--var: value;` declarations per scheme
32
40
  - **W3C DTCG export** — spec-conformant `.tokens.json` (2025.10) for Figma, Tokens Studio, Style Dictionary, and every DTCG tool
33
41
  - **W3C DTCG Resolver-Module export** — opt-in single-document `dtcgResolver()` (sets + a `scheme` modifier with a context per variant) for resolver tools such as Dispersa
34
42
  - **Tailwind CSS v4 export** — `@theme` block + dark / high-contrast overrides
35
- - **Import/Export** — serialize and restore theme configurations
43
+ - **Import/Export** — serialize and restore themes, color tokens, and entire palettes as JSON
36
44
  - **Create from hex/RGB** — start from an existing brand color
37
45
  - **Zero dependencies** — pure math, runs anywhere (Node.js, browser, edge)
38
46
  - **Tree-shakeable ESM + CJS** — dual-format package
@@ -57,45 +65,93 @@ yarn add @tenphi/glaze
57
65
  ```ts
58
66
  import { glaze } from '@tenphi/glaze';
59
67
 
60
- const primary = glaze(280, 80);
61
-
62
- primary.colors({
63
- surface: { tone: 97, saturation: 0.75 },
64
- text: { base: 'surface', tone: '-52', contrast: 'AAA' },
65
- border: { base: 'surface', tone: ['-7', '-20'], contrast: 'AA-large' },
66
- 'accent-fill': { tone: 52, mode: 'fixed' },
67
- 'accent-text': { base: 'accent-fill', tone: 'max', mode: 'fixed' },
68
- 'shadow-md': { type: 'shadow', bg: 'surface', fg: 'text', intensity: 10 },
68
+ glaze.configure({
69
+ modes: { dark: true, highContrast: true },
69
70
  });
70
71
 
71
- const danger = primary.extend({ hue: 23 });
72
- const success = primary.extend({ hue: 157 });
72
+ const defaultTheme = glaze(280, 80);
73
+
74
+ defaultTheme.colors({
75
+ surface: { tone: 97, saturation: 0.15 },
76
+ 'surface-text': {
77
+ base: 'surface',
78
+ tone: '-1',
79
+ contrast: { apca: ['content', 'body'] },
80
+ },
81
+ border: {
82
+ base: 'surface',
83
+ tone: ['-8', '-16'],
84
+ inherit: false,
85
+ },
86
+ 'accent-surface': { tone: 52, mode: 'fixed' },
87
+ 'accent-surface-text': {
88
+ base: 'accent-surface',
89
+ tone: '+1',
90
+ contrast: 'AA',
91
+ mode: 'fixed',
92
+ },
93
+ });
73
94
 
74
- const palette = glaze.palette(
75
- { primary, danger, success },
76
- { primary: 'primary' },
77
- );
95
+ const dangerTheme = defaultTheme.extend({ hue: 23 });
96
+ const palette = glaze.palette({ default: defaultTheme, danger: dangerTheme });
78
97
 
79
- const tokens = palette.tokens();
80
- // → { light: { 'primary-surface': 'okhsl(...)', 'surface': 'okhsl(...)', ... },
81
- // dark: { 'primary-surface': 'okhsl(...)', 'surface': 'okhsl(...)', ... } }
98
+ const tokens = palette.tokens({
99
+ prefix: { default: '', danger: 'danger-' },
100
+ });
101
+ // → {
102
+ // light: { surface: 'oklch(...)', 'danger-surface': 'oklch(...)', ... },
103
+ // dark: { surface: 'oklch(...)', 'danger-surface': 'oklch(...)', ... },
104
+ // lightContrast: { ... },
105
+ // darkContrast: { ... },
106
+ // }
82
107
  ```
83
108
 
84
109
  ## Concepts at a glance
85
110
 
86
- 1. **Theme = one hue/saturation seed.** Status themes are siblings created via `extend()` — they inherit every color definition and only swap the seed.
87
- 2. **Every color is explicit.** A color is either a *root* (absolute `tone`, or `'max'`/`'min'` for an extreme) or *dependent* (`base` + relative offset and/or `contrast`). No implicit roles.
88
- 3. **Tone is contrast-uniform.** `tone` (0–100) replaces OKHSL lightness: equal tone steps give equal WCAG contrast, so ramps are even by construction. See [`docs/okhst.md`](docs/okhst.md).
89
- 4. **`contrast` is a floor, not a target.** A bare number/preset is WCAG; `{ wcag }` / `{ apca }` selects the metric. The solver only shifts a color's tone when the requested position fails the requested floor.
90
- 5. **Light, dark, and high-contrast come from one definition.** `mode` (`auto` / `fixed` / `static`) picks how each color adapts; `tone` / `contrast` / `intensity` / `value` accept an optional `[normal, hc]` pair for explicit high-contrast tuning.
111
+ 1. **A theme has one hue/saturation seed.** Create status themes with
112
+ `extend()` so they inherit the same relationships while changing hue.
113
+ 2. **A color is a root or a dependency.** Roots use an absolute `tone` or
114
+ `'max'`/`'min'`. Dependent colors name a `base` and may use a signed
115
+ **tone delta** such as `'-8'` plus a `contrast` floor.
116
+ 3. **Tone is an authoring axis, not a contrast guarantee.** Equal tone deltas
117
+ are exact WCAG steps for neutrals. Chromatic colors can drift in rendered
118
+ luminance, so Glaze measures contrast floors against the resolved colors.
119
+ 4. **`contrast` is a floor.** A bare number or preset means WCAG;
120
+ `{ wcag }` and `{ apca }` select the metric explicitly. The solver preserves
121
+ the requested tone whenever it already passes.
122
+ 5. **Each color chooses its dark adaptation.** `mode: 'auto'` uses dark tone
123
+ inversion (`100 - t`), `fixed` keeps the authored side of the tone scale,
124
+ and `static` skips scheme adaptation.
125
+ 6. **Tone windows bound ordinary schemes.** `lightTone` and `darkTone`
126
+ configure the light/dark render ranges. High-contrast variants use the full
127
+ range. Values that accept `[normal, highContrast]` pairs can tighten
128
+ deliberately in HC.
129
+
130
+ In Glaze, a **scheme variant** is one of `light`, `dark`, `lightContrast`, or
131
+ `darkContrast`. Export `modes` choose which variants are included; [Tasty](https://tasty.style)
132
+ `states` choose how those variants are activated in an application.
133
+
134
+ ## Choosing an output
135
+
136
+ - `tasty()` returns [Tasty](https://tasty.style) `#token` bindings, including its custom `okhsl()` or
137
+ `okhst()` serialization.
138
+ - `tokens()` and `json()` return JavaScript data and default to native
139
+ `oklch()` values.
140
+ - `css()` returns custom-property declarations.
141
+ - `dtcg()` / `dtcgResolver()` target design-token tooling.
142
+ - `tailwind()` emits a Tailwind CSS v4 theme.
91
143
 
92
144
  ## Documentation
93
145
 
94
- - [`docs/api.md`](docs/api.md) — full API reference (every method, every option).
95
- - [`docs/methodology.md`](docs/methodology.md) — palette design methodology for building from scratch.
96
- - [`docs/migration.md`](docs/migration.md) — wiring tokens into your app and migrating off a legacy color system.
97
- - [`AGENTS.md`](AGENTS.md) — source-tree orientation for contributors.
146
+ - Explore the tone axis in the [interactive playground](https://glaze.tenphi.me/playground/).
147
+ - Start with [the methodology](docs/methodology.md) to design a palette.
148
+ - Use [migration and integration](docs/migration.md) to wire it into an
149
+ application or replace an existing color system.
150
+ - Keep the [API reference](docs/api.md) nearby for every method and option.
151
+ - Read [OKHST in Glaze](docs/okhst.md) for the product-level color model, or
152
+ the [full OKHST specification](https://github.com/tenphi/okhst) for its math.
153
+ - [`AGENTS.md`](https://github.com/tenphi/glaze/blob/main/AGENTS.md) — source-tree orientation for contributors.
98
154
 
99
155
  ## License
100
156
 
101
- [MIT](LICENSE)
157
+ [MIT](https://github.com/tenphi/glaze/blob/main/LICENSE)