@tenphi/glaze 0.0.0-snapshot.c13086f → 0.0.0-snapshot.c61bbdf
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 +158 -0
- package/README.md +106 -1015
- package/dist/index.cjs +3991 -973
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1559 -157
- package/dist/index.d.mts +1559 -157
- package/dist/index.mjs +3959 -973
- package/dist/index.mjs.map +1 -1
- package/docs/contrast.mdx +702 -0
- package/docs/exporting.mdx +624 -0
- package/docs/getting-started.mdx +486 -0
- package/docs/methodology.mdx +973 -0
- package/docs/migration.md +278 -0
- package/docs/mixes.mdx +268 -0
- package/docs/okhst.mdx +596 -0
- package/docs/reference/color-definitions.md +498 -0
- package/docs/reference/color-tokens.md +390 -0
- package/docs/reference/configuration.md +437 -0
- package/docs/reference/contrast.md +276 -0
- package/docs/reference/index.md +218 -0
- package/docs/reference/output.md +594 -0
- package/docs/reference/palette.md +387 -0
- package/docs/reference/theme.md +326 -0
- package/docs/reference/utilities.md +499 -0
- package/docs/schemes.mdx +546 -0
- package/docs/shadows-and-glows.mdx +509 -0
- package/package.json +18 -9
|
@@ -0,0 +1,594 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Output formats
|
|
3
|
+
description: Every output method (tokens, tasty, json, css, dtcg, dtcgResolver, tailwind), its options, color formats, output modes, state aliases, and splitHue.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Output methods turn a resolved theme or palette into color strings or token documents. They exist on [themes](./theme.md) and [palettes](./palette.md) with the same options; palettes add [prefix and primary options](./palette.md#glazepaletteexportoptions). Standalone [color tokens](./color-tokens.md) have single-color versions of the same methods. For choosing an export and wiring it into an app, see [Exporting and integration](../exporting.mdx).
|
|
7
|
+
|
|
8
|
+
Examples on this page use this theme:
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import { glaze } from '@tenphi/glaze';
|
|
12
|
+
|
|
13
|
+
const theme = glaze(280, 80);
|
|
14
|
+
theme.colors({
|
|
15
|
+
surface: { tone: 97 },
|
|
16
|
+
'surface-text': { base: 'surface', tone: '-52', contrast: 'AA' },
|
|
17
|
+
accent: { tone: 52, mode: 'fixed' },
|
|
18
|
+
});
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Output methods at a glance
|
|
22
|
+
|
|
23
|
+
| Method | Returns | Options type | Honors `modes` |
|
|
24
|
+
| --- | --- | --- | --- |
|
|
25
|
+
| [`tokens()`](#tokensoptions) | `{ light: { name: value }, dark: {…}, … }` | `GlazeJsonOptions` | Yes |
|
|
26
|
+
| [`tasty()`](#tastyoptions) | `{ '#name': { '': value, [state]: value } }` | `GlazeTokenOptions` | Yes |
|
|
27
|
+
| [`json()`](#jsonoptions) | `{ name: { light: value, dark: value, … } }` | `GlazeJsonOptions` | Yes |
|
|
28
|
+
| [`css()`](#cssoptions) | `GlazeCssResult` (four strings) | `GlazeCssOptions` | No: all four |
|
|
29
|
+
| [`dtcg()`](#dtcgoptions) | `GlazeDtcgResult` (one document per variant) | `GlazeDtcgOptions` | Yes |
|
|
30
|
+
| [`dtcgResolver()`](#dtcgresolveroptions) | `GlazeDtcgResolverDocument` | `GlazeDtcgResolverOptions` | Yes |
|
|
31
|
+
| [`tailwind()`](#tailwindoptions) | `string` (CSS) | `GlazeTailwindOptions` | Yes |
|
|
32
|
+
|
|
33
|
+
[`format`](#color-formats) applies to every method except `dtcg()` and `dtcgResolver()`: `'oklch'`, `'rgb'`, or `'hsl'`, and on `tasty()` also the Tasty-only `'okhsl'` and `'okhst'`. The two DTCG methods emit structured values and take `colorSpace` instead.
|
|
34
|
+
|
|
35
|
+
Every method resolves the theme first (results are cached; see [`theme.resolve()`](./theme.md#themeresolve)). Tokens are emitted in dependency order.
|
|
36
|
+
|
|
37
|
+
## Scheme variants
|
|
38
|
+
|
|
39
|
+
Glaze always resolves four variants per color. `ResolvedColor` and the results of `tokens()`, `json()`, `css()`, and `dtcg()` key them by the names below. `tasty()` keys them by [state alias](#state-aliases), `tailwind()` by selector, and `dtcgResolver()` uses the names as its default context names, which `contextNames` can rename.
|
|
40
|
+
|
|
41
|
+
| Variant | Scheme | Emitted when |
|
|
42
|
+
| --- | --- | --- |
|
|
43
|
+
| `light` | Light, normal contrast | Always. |
|
|
44
|
+
| `dark` | Dark, normal contrast | `modes.dark` |
|
|
45
|
+
| `lightContrast` | Light, high contrast | `modes.highContrast` |
|
|
46
|
+
| `darkContrast` | Dark, high contrast | `modes.dark` and `modes.highContrast` |
|
|
47
|
+
|
|
48
|
+
How the variants are computed is in [Light, dark, and high contrast](../schemes.mdx).
|
|
49
|
+
|
|
50
|
+
## Output modes
|
|
51
|
+
|
|
52
|
+
`GlazeOutputModes` controls which variants `tokens()`, `tasty()`, `json()`, `dtcg()`, `dtcgResolver()`, and `tailwind()` emit. `css()` ignores it and always returns all four strings.
|
|
53
|
+
|
|
54
|
+
| Field | Type | Default | Description |
|
|
55
|
+
| --- | --- | --- | --- |
|
|
56
|
+
| `dark` | `boolean` | `true` | Include the `dark` variant (and `darkContrast` when `highContrast` is on). |
|
|
57
|
+
| `highContrast` | `boolean` | `false` | Include `lightContrast` (and `darkContrast` when `dark` is on). |
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
// Light only
|
|
61
|
+
theme.tokens({ modes: { dark: false } });
|
|
62
|
+
|
|
63
|
+
// Light + dark (the default)
|
|
64
|
+
theme.tokens({ modes: { highContrast: false } });
|
|
65
|
+
|
|
66
|
+
// All four variants
|
|
67
|
+
theme.tokens({ modes: { dark: true, highContrast: true } });
|
|
68
|
+
// → { light, dark, lightContrast, darkContrast }
|
|
69
|
+
|
|
70
|
+
// Light + light high contrast
|
|
71
|
+
theme.tokens({ modes: { dark: false, highContrast: true } });
|
|
72
|
+
// → { light, lightContrast }
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Each field resolves separately, highest priority first:
|
|
76
|
+
|
|
77
|
+
1. A **global** [`contrastLevel`](./contrast.md#manual-contrast-level) of `100` forces `highContrast: false`, even against an explicit per-call `highContrast: true`. At that level the normal variants already *are* the high-contrast ones, so the tier would duplicate them. Any other level leaves `highContrast` alone.
|
|
78
|
+
2. The per-call `modes` option.
|
|
79
|
+
3. `glaze.configure({ modes })` — the global config ([Configuration](./configuration.md)).
|
|
80
|
+
4. The built-in default `{ dark: true, highContrast: false }`.
|
|
81
|
+
|
|
82
|
+
`modes` is global or per call only — it is not part of a theme's config override. A `contrastLevel` set on one theme or token (rather than globally) never changes which modes are emitted: its high-contrast variants are still emitted when requested, with values equal to its normal ones at level `100`. With a global level of `100`, `css()` still returns all four strings, and `lightContrast` / `darkContrast` equal `light` / `dark`.
|
|
83
|
+
|
|
84
|
+
## Color formats
|
|
85
|
+
|
|
86
|
+
The `format` option picks the color string syntax for `tokens()`, `tasty()`, `json()`, `css()`, and `tailwind()` on themes and palettes, and for the [color token](./color-tokens.md) methods `token()`, `tasty()`, `json()`, `css()`, and `tailwind()`. The default is `'oklch'`.
|
|
87
|
+
|
|
88
|
+
| `format` | Output (alpha = 1) | Output (alpha < 1) | Notes |
|
|
89
|
+
| --- | --- | --- | --- |
|
|
90
|
+
| `'oklch'` (default) | `oklch(L C H)` | `oklch(L C H / A)` | Native CSS. `L` and `C` to 4 decimals, `H` to 2. An achromatic color is `oklch(L 0 0)`. Required for [`splitHue`](#hue-channel-splitting-splithue). |
|
|
91
|
+
| `'rgb'` | `rgb(R G B)` | `rgb(R G B / A)` | Channels 0–255 with up to 2 decimals — not rounded to integers, so 8-bit quantization can't shift a solved contrast. |
|
|
92
|
+
| `'hsl'` | `hsl(H S% L%)` | `hsl(H S% L% / A)` | Up to 2 decimals. |
|
|
93
|
+
| `'okhsl'` | `okhsl(H S% L%)` | `okhsl(H S% L% / A)` | Glaze's native space; not a CSS function. **[Tasty](https://tasty.style) only** (see the guard below). |
|
|
94
|
+
| `'okhst'` | `okhst(H S% T%)` | `okhst(H S% T% / A)` | OKHSL with the [tone](../okhst.mdx) axis. **Tasty only.** |
|
|
95
|
+
|
|
96
|
+
All values use the modern space-separated syntax and strip trailing zeros (`95`, not `95.0`). Alpha is printed to at most 4 decimals.
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
theme.tokens().light.surface;
|
|
100
|
+
// → 'oklch(0.97 0.0128 280)'
|
|
101
|
+
theme.tokens({ format: 'rgb' }).light.surface;
|
|
102
|
+
// → 'rgb(243.08 244.36 253.89)'
|
|
103
|
+
theme.tokens({ format: 'hsl' }).light.surface;
|
|
104
|
+
// → 'hsl(232.87 82.96% 97.45%)'
|
|
105
|
+
theme.tasty({ format: 'okhsl' })['#surface'][''];
|
|
106
|
+
// → 'okhsl(280 80% 96.51%)'
|
|
107
|
+
theme.tasty({ format: 'okhst' })['#surface'][''];
|
|
108
|
+
// → 'okhst(280 80% 97.15%)'
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Native-format guard.** `okhsl` and `okhst` are not CSS color spaces. Only the methods that return Tasty state maps accept them — `tasty()` on themes and palettes, and `token()` / `tasty()` on color tokens. Every other method throws:
|
|
112
|
+
|
|
113
|
+
```text
|
|
114
|
+
glaze: okhst output is only supported by tasty() (not a native CSS
|
|
115
|
+
color space).
|
|
116
|
+
Use tasty({ format: 'okhst' }) or pick a native format (oklch|hsl|rgb)
|
|
117
|
+
for css().
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The guard is exported as [`assertNativeFormat`](./utilities.md#export-guards) for custom pipelines.
|
|
121
|
+
|
|
122
|
+
**Pastel colors.** For a [`pastel`](./color-definitions.md#per-color-pastel) color, `okhsl` and `okhst` output re-express the saturation as its standard (non-pastel) OKHSL equivalent, so parsers that don't know about `pastel` render the same color. The other formats are computed from the gamut-mapped color directly.
|
|
123
|
+
|
|
124
|
+
DTCG output doesn't use `format`; it emits structured `$value` objects. Pick their representation with [`colorSpace`](#dtcgoptions).
|
|
125
|
+
|
|
126
|
+
To format a single resolved variant yourself, use [`glaze.format()`](./utilities.md#glazeformatvariant-format-pastel).
|
|
127
|
+
|
|
128
|
+
## `tokens(options?)`
|
|
129
|
+
|
|
130
|
+
A flat token map grouped by scheme variant. Keys are color names (prefixed in palettes).
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
theme.tokens();
|
|
134
|
+
// → {
|
|
135
|
+
// light: {
|
|
136
|
+
// surface: 'oklch(0.97 0.0128 280)',
|
|
137
|
+
// 'surface-text': 'oklch(0.5286 0.1912 280)',
|
|
138
|
+
// accent: 'oklch(0.5961 0.181 280)',
|
|
139
|
+
// },
|
|
140
|
+
// dark: {
|
|
141
|
+
// surface: 'oklch(0.279 0.1015 280)',
|
|
142
|
+
// 'surface-text': 'oklch(0.6531 0.144 280)',
|
|
143
|
+
// accent: 'oklch(0.5955 0.1601 280)',
|
|
144
|
+
// },
|
|
145
|
+
// }
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
`GlazeJsonOptions`:
|
|
149
|
+
|
|
150
|
+
| Option | Type | Default | Description |
|
|
151
|
+
| --- | --- | --- | --- |
|
|
152
|
+
| `format` | `GlazeColorFormat` | `'oklch'` | `'oklch'`, `'rgb'`, or `'hsl'`. `'okhsl'` / `'okhst'` throw. |
|
|
153
|
+
| `modes` | `GlazeOutputModes` | Global config (`{ dark: true, highContrast: false }`) | Which variants to include. See [Output modes](#output-modes). |
|
|
154
|
+
|
|
155
|
+
Variant keys that are not emitted are absent from the result.
|
|
156
|
+
|
|
157
|
+
## `tasty(options?)`
|
|
158
|
+
|
|
159
|
+
Style-to-state bindings for the [Tasty](https://tasty.style) style system. Keys are `#name` color tokens; each value maps state aliases to colors, with `''` as the default (light) state. Spread the result into component styles or register it as a recipe.
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
theme.tasty();
|
|
163
|
+
// → {
|
|
164
|
+
// '#surface': {
|
|
165
|
+
// '': 'oklch(0.97 0.0128 280)',
|
|
166
|
+
// '@media(prefers-color-scheme: dark)': 'oklch(0.279 0.1015 280)',
|
|
167
|
+
// },
|
|
168
|
+
// '#surface-text': {
|
|
169
|
+
// '': 'oklch(0.5286 0.1912 280)',
|
|
170
|
+
// '@media(prefers-color-scheme: dark)': 'oklch(0.6531 0.144 280)',
|
|
171
|
+
// },
|
|
172
|
+
// '#accent': { … },
|
|
173
|
+
// }
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
`GlazeTokenOptions`:
|
|
177
|
+
|
|
178
|
+
| Option | Type | Default | Description |
|
|
179
|
+
| --- | --- | --- | --- |
|
|
180
|
+
| `format` | `GlazeColorFormat` | `'oklch'` | Any of the five formats, including the Tasty-only `'okhsl'` and `'okhst'`. |
|
|
181
|
+
| `modes` | `GlazeOutputModes` | Global config | Which variants to include. |
|
|
182
|
+
| `states.dark` | `string` | Global `states.dark` (`'@media(prefers-color-scheme: dark)'`) | State alias for the dark variant. |
|
|
183
|
+
| `states.highContrast` | `string` | Global `states.highContrast` (`'@media(prefers-contrast: more)'`) | State alias for the light high-contrast variant. |
|
|
184
|
+
| `splitHue` | `boolean` | `false` | Emit hue as separate `$name-hue` tokens referenced with `var()`. See [`splitHue`](#hue-channel-splitting-splithue). |
|
|
185
|
+
| `name` | `string` | `'theme'` | Base name of the theme-level hue var (`$theme-hue` / `--theme-hue`). Used only with `splitHue`. Palettes use each theme's name instead. |
|
|
186
|
+
| `prefix` | `boolean \| Record<string, string>` | — | Palettes only; ignored by `theme.tasty()`. See [Prefix behavior](./palette.md#prefix-behavior). |
|
|
187
|
+
|
|
188
|
+
### State aliases
|
|
189
|
+
|
|
190
|
+
`tasty()` is the only output that uses state aliases. The defaults are media-query states, so the tokens follow the operating system preference without registering custom states:
|
|
191
|
+
|
|
192
|
+
| Variant | State key |
|
|
193
|
+
| --- | --- |
|
|
194
|
+
| `light` | `''` |
|
|
195
|
+
| `dark` | `states.dark` — default `'@media(prefers-color-scheme: dark)'` |
|
|
196
|
+
| `lightContrast` | `states.highContrast` — default `'@media(prefers-contrast: more)'` |
|
|
197
|
+
| `darkContrast` | `` `${states.dark} & ${states.highContrast}` `` — default `'@media(prefers-color-scheme: dark) & @media(prefers-contrast: more)'` |
|
|
198
|
+
|
|
199
|
+
Set them globally with `glaze.configure({ states })` or per call. Custom states such as `@dark` must be registered in your Tasty config:
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
glaze.configure({
|
|
203
|
+
states: { dark: '@dark', highContrast: '@hc' },
|
|
204
|
+
modes: { highContrast: true },
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
theme.tasty()['#surface'];
|
|
208
|
+
// → {
|
|
209
|
+
// '': 'oklch(0.97 0.0128 280)',
|
|
210
|
+
// '@dark': 'oklch(0.279 0.1015 280)',
|
|
211
|
+
// '@hc': 'oklch(0.9685 0.0134 280)',
|
|
212
|
+
// '@dark & @hc': 'oklch(0.1685 0.0615 280)',
|
|
213
|
+
// }
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
A per-call `states` object overrides only the fields it sets; `theme.tasty({ states: { dark: '@night' } })` keeps the global high-contrast alias.
|
|
217
|
+
|
|
218
|
+
## `json(options?)`
|
|
219
|
+
|
|
220
|
+
A per-color map of variant values.
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
theme.json();
|
|
224
|
+
// → {
|
|
225
|
+
// surface: {
|
|
226
|
+
// light: 'oklch(0.97 0.0128 280)',
|
|
227
|
+
// dark: 'oklch(0.279 0.1015 280)',
|
|
228
|
+
// },
|
|
229
|
+
// 'surface-text': {
|
|
230
|
+
// light: 'oklch(0.5286 0.1912 280)',
|
|
231
|
+
// dark: 'oklch(0.6531 0.144 280)',
|
|
232
|
+
// },
|
|
233
|
+
// accent: {
|
|
234
|
+
// light: 'oklch(0.5961 0.181 280)',
|
|
235
|
+
// dark: 'oklch(0.5955 0.1601 280)',
|
|
236
|
+
// },
|
|
237
|
+
// }
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Same options as [`tokens()`](#tokensoptions) (`GlazeJsonOptions`). The result holds resolved strings; it is not an authoring snapshot — use [`theme.export()`](./theme.md#themeexportoverride) for that. [`palette.json()`](./palette.md#palettejson) groups by theme name.
|
|
241
|
+
|
|
242
|
+
## `css(options?)`
|
|
243
|
+
|
|
244
|
+
CSS custom property declarations, one string per variant. Each string is newline-separated `--<name><suffix>: <value>;` lines, ready to drop into a rule.
|
|
245
|
+
|
|
246
|
+
```ts
|
|
247
|
+
theme.css();
|
|
248
|
+
// → {
|
|
249
|
+
// light:
|
|
250
|
+
// '--surface-color: oklch(0.97 0.0128 280);\n' +
|
|
251
|
+
// '--surface-text-color: oklch(0.5286 0.1912 280);\n' +
|
|
252
|
+
// '--accent-color: oklch(0.5961 0.181 280);',
|
|
253
|
+
// dark:
|
|
254
|
+
// '--surface-color: oklch(0.279 0.1015 280);\n' +
|
|
255
|
+
// '--surface-text-color: oklch(0.6531 0.144 280);\n' +
|
|
256
|
+
// '--accent-color: oklch(0.5955 0.1601 280);',
|
|
257
|
+
// lightContrast: '--surface-color: oklch(0.9685 0.0134 280);\n…',
|
|
258
|
+
// darkContrast: '--surface-color: oklch(0.1685 0.0615 280);\n…',
|
|
259
|
+
// }
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
`GlazeCssOptions`:
|
|
263
|
+
|
|
264
|
+
| Option | Type | Default | Description |
|
|
265
|
+
| --- | --- | --- | --- |
|
|
266
|
+
| `format` | `GlazeColorFormat` | `'oklch'` | `'oklch'`, `'rgb'`, or `'hsl'`. `'okhsl'` / `'okhst'` throw. |
|
|
267
|
+
| `suffix` | `string` | `'-color'` | Appended to each property name. Pass `''` for bare names (`--surface`). |
|
|
268
|
+
| `splitHue` | `boolean` | `false` | Emit hue as separate `--*-hue` properties referenced with `var()`. See [`splitHue`](#hue-channel-splitting-splithue). |
|
|
269
|
+
| `name` | `string` | `'theme'` | Base name of the theme-level hue var (`--theme-hue`). Used only with `splitHue`. Palettes use each theme's name instead. |
|
|
270
|
+
|
|
271
|
+
`GlazeCssResult` always has all four keys — `light`, `dark`, `lightContrast`, `darkContrast` — whatever `modes` says. A key is `''` when the theme has no colors. Which selectors or media queries to wrap each string in is up to you; [Exporting and integration](../exporting.mdx) shows common setups.
|
|
272
|
+
|
|
273
|
+
## `dtcg(options?)`
|
|
274
|
+
|
|
275
|
+
W3C [Design Tokens Format Module (2025.10)](https://www.designtokens.org/) documents — the vendor-neutral JSON format read by Figma, Tokens Studio, Style Dictionary v4+, Terrazzo, Penpot, and other DTCG tools. Returns one spec-conformant token tree per scheme variant.
|
|
276
|
+
|
|
277
|
+
```ts
|
|
278
|
+
theme.dtcg();
|
|
279
|
+
// → {
|
|
280
|
+
// light: {
|
|
281
|
+
// surface: {
|
|
282
|
+
// $type: 'color',
|
|
283
|
+
// $value: {
|
|
284
|
+
// colorSpace: 'srgb',
|
|
285
|
+
// components: [0.953256, 0.958291, 0.995646],
|
|
286
|
+
// hex: '#f3f4fe',
|
|
287
|
+
// },
|
|
288
|
+
// },
|
|
289
|
+
// …
|
|
290
|
+
// },
|
|
291
|
+
// dark: {
|
|
292
|
+
// surface: {
|
|
293
|
+
// $type: 'color',
|
|
294
|
+
// $value: {
|
|
295
|
+
// colorSpace: 'srgb',
|
|
296
|
+
// components: [0.130873, 0.118318, 0.346863],
|
|
297
|
+
// hex: '#211e58',
|
|
298
|
+
// },
|
|
299
|
+
// },
|
|
300
|
+
// …
|
|
301
|
+
// },
|
|
302
|
+
// }
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Write each document to its own `.tokens.json` file. One file per scheme is the most tool-compatible convention: one per Style Dictionary theme, Tokens Studio set, or Figma variable mode.
|
|
306
|
+
|
|
307
|
+
`GlazeDtcgOptions`:
|
|
308
|
+
|
|
309
|
+
| Option | Type | Default | Description |
|
|
310
|
+
| --- | --- | --- | --- |
|
|
311
|
+
| `colorSpace` | `DtcgColorSpace` | `'srgb'` | `'srgb'` emits gamma sRGB `components` (0–1) plus a `hex` hint — understood everywhere. `'oklch'` emits `[L, C, H]` components and no `hex` — Glaze-native and wide-gamut. |
|
|
312
|
+
| `modes` | `GlazeOutputModes` | Global config | Which variants to include. `light` is always present. |
|
|
313
|
+
|
|
314
|
+
`GlazeDtcgResult` has `light` (always) and `dark`, `lightContrast`, `darkContrast`, each `undefined` when its mode is off.
|
|
315
|
+
|
|
316
|
+
Token shapes:
|
|
317
|
+
|
|
318
|
+
| Type | Shape |
|
|
319
|
+
| --- | --- |
|
|
320
|
+
| `DtcgColorToken` | `{ $type: 'color', $value: DtcgColorValue, $description?: string }`. `$type` is always `'color'`; Glaze never writes `$description`. |
|
|
321
|
+
| `DtcgSrgbColorValue` | `{ colorSpace: 'srgb', components: [r, g, b], hex: '#rrggbb', alpha?: number }`. Components rounded to 6 decimals; `hex` is 6-digit lowercase and never carries alpha. |
|
|
322
|
+
| `DtcgOklchColorValue` | `{ colorSpace: 'oklch', components: [L, C, H], alpha?: number }`. `L` and `C` rounded to 6 decimals, `H` to 4. |
|
|
323
|
+
| `DtcgColorValue` | `DtcgSrgbColorValue \| DtcgOklchColorValue`. |
|
|
324
|
+
| `DtcgDocument` | `Record<string, DtcgColorToken>` — one flat tree per scheme. |
|
|
325
|
+
|
|
326
|
+
`alpha` (rounded to 6 decimals) is present only when the color's opacity is below 1.
|
|
327
|
+
|
|
328
|
+
```ts
|
|
329
|
+
theme.dtcg({ colorSpace: 'oklch' }).light.surface;
|
|
330
|
+
// → {
|
|
331
|
+
// $type: 'color',
|
|
332
|
+
// $value: {
|
|
333
|
+
// colorSpace: 'oklch',
|
|
334
|
+
// components: [0.970004, 0.012759, 280],
|
|
335
|
+
// },
|
|
336
|
+
// }
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
## `dtcgResolver(options?)`
|
|
340
|
+
|
|
341
|
+
One W3C [DTCG Resolver-Module](https://www.designtokens.org/) document that describes **every scheme variant in one file** — an alternative to `dtcg()`'s per-scheme files for tools that resolve sets and modifiers (for example Dispersa). The light document becomes `sets.base.sources[0]`, the default context; each other emitted variant becomes a context override on a single `scheme` modifier.
|
|
342
|
+
|
|
343
|
+
```ts
|
|
344
|
+
theme.dtcgResolver({ modes: { highContrast: true } });
|
|
345
|
+
// → {
|
|
346
|
+
// version: '2025.10',
|
|
347
|
+
// sets: {
|
|
348
|
+
// base: {
|
|
349
|
+
// sources: [
|
|
350
|
+
// {
|
|
351
|
+
// surface: {
|
|
352
|
+
// $type: 'color',
|
|
353
|
+
// $value: {
|
|
354
|
+
// colorSpace: 'srgb',
|
|
355
|
+
// components: [0.953256, 0.958291, 0.995646],
|
|
356
|
+
// hex: '#f3f4fe',
|
|
357
|
+
// },
|
|
358
|
+
// },
|
|
359
|
+
// …
|
|
360
|
+
// },
|
|
361
|
+
// ],
|
|
362
|
+
// },
|
|
363
|
+
// },
|
|
364
|
+
// modifiers: {
|
|
365
|
+
// scheme: {
|
|
366
|
+
// default: 'light',
|
|
367
|
+
// contexts: {
|
|
368
|
+
// light: [],
|
|
369
|
+
// dark: [{ surface: { $type: 'color', $value: {…} }, … }],
|
|
370
|
+
// lightContrast: [{ surface: {…}, … }],
|
|
371
|
+
// darkContrast: [{ surface: {…}, … }],
|
|
372
|
+
// },
|
|
373
|
+
// },
|
|
374
|
+
// },
|
|
375
|
+
// resolutionOrder: [
|
|
376
|
+
// { $ref: '#/sets/base' },
|
|
377
|
+
// { $ref: '#/modifiers/scheme' },
|
|
378
|
+
// ],
|
|
379
|
+
// }
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
**Why one modifier with four contexts.** Glaze resolves `darkContrast` independently — it is not `dark` plus `lightContrast` layered. The resolver model composes modifiers additively (the last in `resolutionOrder` wins on conflict), so two independent modifiers (`scheme` × `contrast`) would produce wrong values for the dark high-contrast combination. One `scheme` modifier with a context per variant keeps every resolved value exact. Choose `dtcgResolver()` for single-file theming fed to a resolver tool, and `dtcg()` for the widest per-file tool compatibility.
|
|
383
|
+
|
|
384
|
+
`GlazeDtcgResolverOptions` extends `GlazeDtcgOptions`:
|
|
385
|
+
|
|
386
|
+
| Option | Type | Default | Description |
|
|
387
|
+
| --- | --- | --- | --- |
|
|
388
|
+
| `colorSpace` | `DtcgColorSpace` | `'srgb'` | Same as [`dtcg()`](#dtcgoptions); applies to every source and context. |
|
|
389
|
+
| `modes` | `GlazeOutputModes` | Global config | Which variants become contexts. `light` is always present (it is the default context, with an empty override list); variants that are off are omitted. |
|
|
390
|
+
| `setName` | `string` | `'base'` | Name of the single set holding the light token tree. |
|
|
391
|
+
| `modifierName` | `string` | `'scheme'` | Name of the modifier for the scheme axis. |
|
|
392
|
+
| `contextNames` | `GlazeDtcgResolverContextNames` | Variant names | Rename any of the four contexts, e.g. `{ dark: 'night' }`. Fields: `light`, `dark`, `lightContrast`, `darkContrast`. |
|
|
393
|
+
| `version` | `string` | `'2025.10'` | The document's `version` field. |
|
|
394
|
+
|
|
395
|
+
`setName` and `modifierName` also flow into `resolutionOrder` (`#/sets/<setName>`, `#/modifiers/<modifierName>`), and a renamed `light` context is also the modifier's `default`.
|
|
396
|
+
|
|
397
|
+
Document types:
|
|
398
|
+
|
|
399
|
+
| Type | Shape |
|
|
400
|
+
| --- | --- |
|
|
401
|
+
| `GlazeDtcgResolverDocument` | `{ version: string, sets: Record<string, DtcgResolverSet>, modifiers: Record<string, DtcgResolverModifier>, resolutionOrder: DtcgResolverRef[] }` |
|
|
402
|
+
| `DtcgResolverSet` | `{ sources: DtcgTokenTree[] }` |
|
|
403
|
+
| `DtcgResolverModifier` | `{ default: string, contexts: Record<string, DtcgTokenTree[]> }` |
|
|
404
|
+
| `DtcgResolverRef` | `{ $ref: string }` — a JSON pointer. |
|
|
405
|
+
| `DtcgTokenTree` | `{ [key: string]: DtcgColorToken \| DtcgTokenTree }`. Glaze's flat `DtcgDocument` is a valid shallow tree. |
|
|
406
|
+
|
|
407
|
+
## `tailwind(options?)`
|
|
408
|
+
|
|
409
|
+
A Tailwind CSS v4 `@theme` block (the light baseline) plus dark and high-contrast overrides under configurable selectors, returned as one ready-to-paste CSS string. The `--color-*` namespace makes Tailwind generate `bg-*`, `text-*`, `border-*`, and the other color utilities.
|
|
410
|
+
|
|
411
|
+
```css
|
|
412
|
+
/* theme.tailwind({ modes: { highContrast: true } }) */
|
|
413
|
+
@theme {
|
|
414
|
+
--color-surface: oklch(0.97 0.0128 280);
|
|
415
|
+
--color-surface-text: oklch(0.5286 0.1912 280);
|
|
416
|
+
--color-accent: oklch(0.5961 0.181 280);
|
|
417
|
+
}
|
|
418
|
+
.dark {
|
|
419
|
+
--color-surface: oklch(0.279 0.1015 280);
|
|
420
|
+
--color-surface-text: oklch(0.6531 0.144 280);
|
|
421
|
+
--color-accent: oklch(0.5955 0.1601 280);
|
|
422
|
+
}
|
|
423
|
+
.high-contrast {
|
|
424
|
+
--color-surface: oklch(0.9685 0.0134 280);
|
|
425
|
+
--color-surface-text: oklch(0.4426 0.178 280);
|
|
426
|
+
--color-accent: oklch(0.5784 0.1853 280);
|
|
427
|
+
}
|
|
428
|
+
.dark.high-contrast {
|
|
429
|
+
--color-surface: oklch(0.1685 0.0615 280);
|
|
430
|
+
--color-surface-text: oklch(0.7012 0.1258 280);
|
|
431
|
+
--color-accent: oklch(0.5784 0.1633 280);
|
|
432
|
+
}
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
`GlazeTailwindOptions`:
|
|
436
|
+
|
|
437
|
+
| Option | Type | Default | Description |
|
|
438
|
+
| --- | --- | --- | --- |
|
|
439
|
+
| `format` | `GlazeColorFormat` | `'oklch'` | `'oklch'`, `'rgb'`, or `'hsl'`. `'okhsl'` / `'okhst'` throw. |
|
|
440
|
+
| `namespace` | `string` | `'color-'` | Custom property namespace, forming `--<namespace><name>` (e.g. `--color-surface`). Named `namespace` so it doesn't clash with the palette's theme `prefix` option. |
|
|
441
|
+
| `darkSelector` | `string` | `'.dark'` | Selector wrapping the dark overrides. An at-rule such as `'@media (prefers-color-scheme: dark)'` drives dark mode from the OS preference. |
|
|
442
|
+
| `highContrastSelector` | `string` | `'.high-contrast'` | Selector wrapping the light high-contrast overrides. |
|
|
443
|
+
| `modes` | `GlazeOutputModes` | Global config | Which override blocks to include. The `@theme` block is always emitted when the theme has colors. |
|
|
444
|
+
|
|
445
|
+
How the override blocks are built:
|
|
446
|
+
|
|
447
|
+
- Blocks appear in the order `@theme`, dark, high contrast, dark high contrast.
|
|
448
|
+
- The dark high-contrast block combines both selectors. Class-like selectors concatenate into one compound selector (`.dark.high-contrast`).
|
|
449
|
+
- An at-rule (any selector starting with `@`) wraps the block instead: Glaze nests the remaining selectors inside it, or `:root` when there are none.
|
|
450
|
+
- A theme with no colors returns `''`.
|
|
451
|
+
|
|
452
|
+
```ts
|
|
453
|
+
theme.tailwind({
|
|
454
|
+
darkSelector: '@media (prefers-color-scheme: dark)',
|
|
455
|
+
modes: { highContrast: true },
|
|
456
|
+
});
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
```css
|
|
460
|
+
/* The override blocks only; the @theme block is unchanged. */
|
|
461
|
+
@media (prefers-color-scheme: dark) {
|
|
462
|
+
:root {
|
|
463
|
+
--color-surface: oklch(0.279 0.1015 280);
|
|
464
|
+
/* … */
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
.high-contrast {
|
|
468
|
+
--color-surface: oklch(0.9685 0.0134 280);
|
|
469
|
+
/* … */
|
|
470
|
+
}
|
|
471
|
+
@media (prefers-color-scheme: dark) {
|
|
472
|
+
.high-contrast {
|
|
473
|
+
--color-surface: oklch(0.1685 0.0615 280);
|
|
474
|
+
/* … */
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
## Hue channel splitting (`splitHue`)
|
|
480
|
+
|
|
481
|
+
With `splitHue: true` and `format: 'oklch'`, Glaze emits hue as its own custom property and references it with `var()` in the color values, so you can re-skin colors at runtime by changing one variable.
|
|
482
|
+
|
|
483
|
+
Supported on `theme.css()`, `theme.tasty()`, `palette.css()`, `palette.tasty()`, and the standalone [`token.css()`](./color-tokens.md). Not supported on `token.token()` / `token.tasty()`, whose return shape can't carry the hue declaration, nor on `tokens()`, `json()`, `dtcg()`, `dtcgResolver()`, or `tailwind()`.
|
|
484
|
+
|
|
485
|
+
```ts
|
|
486
|
+
const theme = glaze(
|
|
487
|
+
{ hue: 240, saturation: 18, darkHue: 200 },
|
|
488
|
+
{ pastel: true },
|
|
489
|
+
);
|
|
490
|
+
theme.colors({
|
|
491
|
+
surface: { tone: 97 },
|
|
492
|
+
accent: { tone: 52, hue: '+20' },
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
theme.css({ splitHue: true, name: 'brand' });
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
```css
|
|
499
|
+
/* light */
|
|
500
|
+
--brand-hue: 240;
|
|
501
|
+
--accent-hue: calc(var(--brand-hue) + 20);
|
|
502
|
+
--surface-color: oklch(0.97 0.0025 var(--brand-hue));
|
|
503
|
+
--accent-color: oklch(0.5961 0.0182 var(--accent-hue));
|
|
504
|
+
|
|
505
|
+
/* dark */
|
|
506
|
+
--brand-hue: 200;
|
|
507
|
+
--accent-hue: calc(var(--brand-hue) + 20);
|
|
508
|
+
--surface-color: oklch(0.279 0.0077 var(--brand-hue));
|
|
509
|
+
--accent-color: oklch(0.5699 0.0157 var(--accent-hue));
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
### Requirements
|
|
513
|
+
|
|
514
|
+
- **`format: 'oklch'`.** CSS `var()` can stand in for the hue slot of `oklch()` only. With any other format `splitHue` is ignored and colors are emitted inline, without an error.
|
|
515
|
+
- **Every exported color is pastel.** Set `pastel: true` on the theme or token override, or per color. Pastel mode bounds chroma by the hue-independent safe chroma at each lightness, so the emitted `C` stays inside sRGB for any rotated hue. A non-pastel color in any emitted variant throws rather than emit values that would clip under rotation:
|
|
516
|
+
|
|
517
|
+
```text
|
|
518
|
+
glaze: splitHue requires every color to be pastel (hue rotation is
|
|
519
|
+
only clip-free when chroma is bounded by the hue-independent safe
|
|
520
|
+
chroma).
|
|
521
|
+
Non-pastel: surface, accent.
|
|
522
|
+
Set pastel: true (per-theme, per-token, or per-color) or drop splitHue.
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
The check is exported as [`assertAllPastel`](./utilities.md#export-guards).
|
|
526
|
+
|
|
527
|
+
### Variable names
|
|
528
|
+
|
|
529
|
+
| Variable | CSS name | Tasty token |
|
|
530
|
+
| --- | --- | --- |
|
|
531
|
+
| Theme hue | `--<name>-hue` (`name` defaults to `'theme'`) | `$<name>-hue` |
|
|
532
|
+
| Per-color hue | `--<prefix><color>-hue` | `$<prefix><color>-hue` |
|
|
533
|
+
|
|
534
|
+
In `tasty()` the hue declarations become `$…-hue` keys next to the `#color` keys, with the same state map shape. In palettes `<name>` is the theme name and `<prefix>` the theme's prefix; see [`splitHue` in palettes](./palette.md#splithue-in-palettes).
|
|
535
|
+
|
|
536
|
+
> [!WARNING]
|
|
537
|
+
> Theme and per-color vars share one namespace. A color named the same as `name` (a `brand` color with `name: 'brand'`) produces `--brand-hue` twice; only the theme var is kept, so that color silently renders at the theme hue. Pick a `name` that no color uses.
|
|
538
|
+
|
|
539
|
+
### How each color references hue
|
|
540
|
+
|
|
541
|
+
| Color | Hue reference |
|
|
542
|
+
| --- | --- |
|
|
543
|
+
| Regular color with no `hue`, `darkHue`, or `from` | `var(--<name>-hue)` directly; no per-color var. |
|
|
544
|
+
| Relative `hue: '+N'` / `'-N'` | Per-color var `calc(var(--<name>-hue) + N)` (or `- N`). |
|
|
545
|
+
| Absolute `hue: N` | Per-color var set to `N`, normalized to 0–360. |
|
|
546
|
+
| Only `darkHue` set | Per-color var: `var(--<name>-hue)` in light, the dark value in dark. A color that authors a hue in either scheme gets its own var in both, so the shared `var()` reference stays valid. |
|
|
547
|
+
| `from` literal color | Per-color var pinned to the color's resolved hue, so re-skinning the theme doesn't re-tint a literal brand color. |
|
|
548
|
+
| `mode: 'static'` while the theme has a dark seed hue different from `hue` | Per-color var pinned to the resolved hue, so the color doesn't drift with the dark seed. |
|
|
549
|
+
| Shadow, glow, and mix colors | Inline full `oklch()` value; they don't follow hue rotation. |
|
|
550
|
+
| Achromatic (saturation 0) | Inline `oklch(L 0 0)`. |
|
|
551
|
+
|
|
552
|
+
### Dark hues
|
|
553
|
+
|
|
554
|
+
Hue vars are scheme-independent unless the theme or a color authors a dark hue (`darkHue` on the [seed](./theme.md#glazethemeseed) or on a [color definition](./color-definitions.md#dark-seed-darkhue--darksaturation)). When the dark set differs from the light one, Glaze re-declares the **whole** hue set for dark: in `css()` at the top of the `dark` and `darkContrast` strings, in `tasty()` under the dark state (only when `modes.dark` is on). The `lightContrast` string never carries hue declarations; it inherits the light ones.
|
|
555
|
+
|
|
556
|
+
`--accent-hue` in the example repeats verbatim on purpose. A custom property substitutes `var()` at computed-value time on the element that declares it, so a per-color var has to be re-declared next to the new `--brand-hue` to pick it up.
|
|
557
|
+
|
|
558
|
+
### Planning types
|
|
559
|
+
|
|
560
|
+
`HuePlan`, `HueDeclaration`, and `ChannelCtx` are exported for custom pipelines that reproduce this splitting:
|
|
561
|
+
|
|
562
|
+
| Type | Fields |
|
|
563
|
+
| --- | --- |
|
|
564
|
+
| `HueDeclaration` | `prop` — the custom property name (`--accent-hue`); `value` — its value (`240`, `calc(var(--theme-hue) + 20)`). |
|
|
565
|
+
| `HuePlan` | `hueVar` — the `var()` reference spliced into `oklch(L C <hueVar>)`; `inline` — `true` when the color is emitted as a full inline value; `declarations` — the hue declarations backing it. |
|
|
566
|
+
| `ChannelCtx` | `seedHue`, optional `darkSeedHue`, `baseName` (theme var name without `--` / `-hue`), `prefix`, `defs` (the `ColorMap`), `mode` (`'theme'` or `'standalone'`), optional `resolvedHue` (standalone tokens), optional `emitDeclarations` (default `true`; `false` for a pass that only references vars another pass declares, like a palette's primary aliases). |
|
|
567
|
+
|
|
568
|
+
## Translucent colors
|
|
569
|
+
|
|
570
|
+
Shadow and glow colors, transparent mixes, and colors with `opacity` resolve with `alpha` below 1. String formats append it as `/ A`; DTCG adds an `alpha` field:
|
|
571
|
+
|
|
572
|
+
```ts
|
|
573
|
+
theme.colors({
|
|
574
|
+
'accent-pressed': { type: 'shadow', bg: 'accent', intensity: 10 },
|
|
575
|
+
scrim: { tone: 0, saturation: 0, mode: 'static', opacity: 0.5 },
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
theme.tokens({ format: 'rgb' }).light;
|
|
579
|
+
// → {
|
|
580
|
+
// …,
|
|
581
|
+
// 'accent-pressed': 'rgb(0 0 0 / 0.1697)',
|
|
582
|
+
// scrim: 'rgb(0 0 0 / 0.5)',
|
|
583
|
+
// }
|
|
584
|
+
|
|
585
|
+
theme.dtcg().light['accent-pressed'].$value;
|
|
586
|
+
// → {
|
|
587
|
+
// colorSpace: 'srgb',
|
|
588
|
+
// components: [0, 0, 0],
|
|
589
|
+
// hex: '#000000',
|
|
590
|
+
// alpha: 0.16972,
|
|
591
|
+
// }
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
A shadow's or glow's alpha is solved for its `bg` surface, and a transparent mix's alpha is its `value` (or the value solved for `contrast`) over its `base`; composite each over that color. An `opacity` color carries its authored alpha unchanged in every scheme. See [Shadows and glows](../shadows-and-glows.mdx) and [Mixes and transparency](../mixes.mdx).
|