@tenphi/glaze 2.0.1 → 3.0.0

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/docs/okhst.md DELETED
@@ -1,207 +0,0 @@
1
- # OKHST in Glaze
2
-
3
- Glaze uses [OKHST](https://github.com/tenphi/okhst), an OKHSL-derived color
4
- space with a tone axis for stable palette authoring. This page explains how
5
- Glaze applies the model. The standalone repository is the canonical
6
- specification for the transfer functions, derivation, and color-model
7
- invariants.
8
-
9
- For practical palette design, continue to [methodology.md](methodology.md). For
10
- every related option and utility, see [api.md](api.md).
11
-
12
- ## What tone means
13
-
14
- OKHST keeps OKHSL hue and saturation and replaces authored lightness with
15
- **tone**:
16
-
17
- | Space | Coordinates | Third coordinate |
18
- | ----- | ----------- | -------------------- |
19
- | OKHSL | `h, s, l` | Perceptual lightness |
20
- | OKHST | `h, s, t` | Contrast-shaped tone |
21
-
22
- Tone uses a `0–100` authoring scale. Its primary invariant is:
23
-
24
- > The same tone maps to the same OKHSL lightness for every hue and saturation.
25
-
26
- For neutral colors, equal tone differences produce equal WCAG contrast ratios.
27
- For chromatic colors, they remain a useful progression but are not exact:
28
- hue, saturation, and gamut mapping can change the final sRGB luminance.
29
-
30
- This distinction informs two authoring rules:
31
-
32
- - Use **tone deltas** for visual spacing within ramps and related UI colors.
33
- - Use `contrast` when a rendered color must meet a measured WCAG or APCA floor.
34
-
35
- ## Tone vocabulary
36
-
37
- ### Absolute tone
38
-
39
- A numeric `tone` places a color independently on the `0–100` authoring scale:
40
-
41
- ```ts
42
- surface: {
43
- tone: 97;
44
- }
45
- ```
46
-
47
- `'max'` and `'min'` select the corresponding end of the active scheme. With a
48
- `base`, they instead preserve their distance from that base across schemes —
49
- see [Scheme adaptation](#scheme-adaptation).
50
-
51
- ### Tone delta
52
-
53
- A **tone delta** is the signed difference between a dependent color and its
54
- base. It is authored as a string:
55
-
56
- ```ts
57
- border: { base: 'surface', tone: '-8' }
58
- ```
59
-
60
- Here, `-8` is the tone delta. The base is resolved separately for every scheme,
61
- then the delta is applied to that resolved base. This keeps related colors
62
- visually ordered across light, dark, and high-contrast variants.
63
-
64
- ### Tone window
65
-
66
- A **tone window** is the scheme-specific render range configured by
67
- `lightTone` or `darkTone`:
68
-
69
- ```ts
70
- glaze.configure({
71
- lightTone: [10, 100],
72
- darkTone: [15, 95],
73
- });
74
- ```
75
-
76
- In Glaze, `lo` and `hi` are OKHSL-lightness boundaries. Authored tone is
77
- positioned within the corresponding tone interval, then converted back to
78
- OKHSL lightness for rendering. The object form adds an advanced render
79
- curvature:
80
-
81
- ```ts
82
- lightTone: { lo: 10, hi: 100, eps: 0.05 }
83
- ```
84
-
85
- Pass `false` to use the full range. High-contrast variants always bypass the
86
- ordinary boundaries and use the full range.
87
-
88
- A [manual contrast level](api.md#manual-contrast-level) generalizes that bypass
89
- continuously: with `contrastLevel: L` (`f = L / 100`), each boundary moves toward
90
- the full range instead of jumping to it —
91
-
92
- ```text
93
- lo(f) = lo + (0 - lo) * f
94
- hi(f) = hi + (100 - hi) * f
95
- ```
96
-
97
- — so the light default walks `[10, 100] → [0, 100]` and the dark default
98
- `[15, 95] → [0, 100]`. The render `eps` is not interpolated, and a `false` window
99
- is already the full range and therefore level-invariant.
100
-
101
- This continuous form applies to the normal variants only. High-contrast variants
102
- are already at the full range, so the level does not widen them further — the
103
- bypass above is what makes the high-contrast tier level-independent.
104
-
105
- ## Scheme adaptation
106
-
107
- Each regular color has an adaptation `mode`:
108
-
109
- | Mode | Light scheme | Dark scheme |
110
- | ---------- | ------------------------------ | -------------------------------------------------------------------- |
111
- | `'auto'` | Map into the light tone window | Apply dark tone inversion (`100 - t`), then map into the dark window |
112
- | `'fixed'` | Map into the light tone window | Map into the dark window without inversion |
113
- | `'static'` | Keep the authored tone | Keep the authored tone |
114
-
115
- Use `auto` for surfaces and foregrounds that should exchange light/dark
116
- positions. Use `fixed` for brand fills and inverse surfaces that should stay on
117
- the same side of the scale. Reserve `static` for colors that must not adapt.
118
-
119
- **Dark tone inversion** is different from `autoFlip`. Inversion is normal
120
- scheme adaptation controlled by `mode`. `autoFlip` handles a different local
121
- problem: it may reverse an overshooting tone delta or let the contrast solver
122
- try the opposite side of a base.
123
-
124
- The regular pipeline is:
125
-
126
- ```text
127
- authored tone
128
- -> choose the color's adaptation mode
129
- -> invert for dark + auto
130
- -> map through the active tone window
131
- -> store canonical tone
132
- -> convert to OKHSL at the rendering edge
133
- ```
134
-
135
- Dark schemes also apply `darkDesaturation` unless the color is `static`.
136
-
137
- **Hue and saturation are authoring inputs, not part of the tone transfer.** The
138
- tone axis is the only channel OKHST adapts across schemes; `h` and `s` pass
139
- through verbatim, with `darkDesaturation` as the one automatic adjustment.
140
- A theme (or a single color) can seed a different hue and saturation for the
141
- dark schemes via `darkHue` / `darkSaturation` — see
142
- [api.md](api.md#dark-seed-darkhue--darksaturation). Those pick the `h` and `s`
143
- that enter the pipeline above; they never change how tone is transferred.
144
- Authoring a dark saturation replaces the `darkDesaturation` step rather than
145
- stacking with it, and `static` colors ignore both.
146
-
147
- **Extremes with a base** take a different dark path. Because the light and dark
148
- windows are asymmetric, running an extreme through the dark window compresses
149
- the span between it and its base, which lowers contrast. For a color with both
150
- `base` and `tone: 'max'` / `'min'`, Glaze measures the tone shift the light
151
- scheme produced between the two, then replays it against the base's resolved
152
- dark tone (mirrored under `auto`, same-signed under `fixed`). Only `[0, 100]`
153
- clamping applies, so such a color may render past the `darkTone` boundary.
154
-
155
- ## Reference and render epsilon
156
-
157
- `REF_EPS = 0.05` defines Glaze's canonical tone scale. It is used for OKHST
158
- input, stored resolved tone, tone deltas, and contrast search.
159
-
160
- An object tone window may supply another `eps` as a scheme-specific rendering
161
- control. Glaze still converts the result back to the reference scale so tone
162
- deltas and contrast calculations remain comparable between schemes. Most
163
- palettes should use the default.
164
-
165
- ## Contrast verification
166
-
167
- Tone provides an efficient starting point for contrast solving, but Glaze does
168
- not assume a chromatic tone has the neutral color's luminance. For a color with
169
- `base` and `contrast`, Glaze:
170
-
171
- 1. resolves both colors for the active scheme;
172
- 2. measures the requested WCAG ratio or APCA Lc;
173
- 3. searches tone when the requested position misses the floor; and
174
- 4. warns when the target is physically unreachable or rendered chroma drifts
175
- below the expected result.
176
-
177
- `contrast` is therefore a floor, not a replacement for tone relationships.
178
- Detailed presets, HC promotion, polarity, and role inference are documented
179
- under [`contrast`](api.md#contrast-floor) and [roles](api.md#roles).
180
-
181
- ## Input and output
182
-
183
- Glaze accepts OKHST through:
184
-
185
- ```ts
186
- glaze.color('okhst(152 95% 70%)');
187
- glaze.color({ h: 152, s: 0.95, t: 0.7 });
188
- glaze.color({ hue: 152, saturation: 95, tone: 70 });
189
- ```
190
-
191
- There is no native CSS `okhst()` function. Native exports use `oklch`, `rgb`,
192
- or `hsl`. [Tasty](https://tasty.style) integrations may request `format: 'okhst'` because it can
193
- consume Glaze's custom serialization:
194
-
195
- ```ts
196
- theme.tasty({ format: 'okhst' });
197
- ```
198
-
199
- ## Bringing existing colors into Glaze
200
-
201
- When moving raw CSS colors or design tokens into Glaze, use `fromHex()`,
202
- `fromRgb()`, or `glaze.color()` to preserve the source color while you establish
203
- theme seeds and semantic relationships. Then replace one-off values with roots,
204
- tone deltas, and contrast floors as appropriate.
205
-
206
- See [migration.md](migration.md#migrating-from-an-existing-color-system) for
207
- the implementation workflow.