@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,390 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Color tokens
|
|
3
|
+
description: glaze.color() standalone tokens — input forms, color values, defaults, methods, config overrides, pairing, and snapshots.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`glaze.color()` creates a single adaptive color without a theme. A token resolves the same four variants as a theme color (`light`, `dark`, `lightContrast`, `darkContrast`) and has the same output methods. For a guided introduction, see [Getting started](../getting-started.mdx#one-color-without-a-theme); for contrast between tokens, see [Contrast solving](../contrast.mdx#pairing-standalone-colors).
|
|
7
|
+
|
|
8
|
+
```ts
|
|
9
|
+
import { glaze } from '@tenphi/glaze';
|
|
10
|
+
import type {
|
|
11
|
+
GlazeColorToken,
|
|
12
|
+
GlazeColorValue,
|
|
13
|
+
GlazeColorInput,
|
|
14
|
+
GlazeFromInput,
|
|
15
|
+
} from '@tenphi/glaze';
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## `glaze.color(input, config?)`
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
glaze.color(
|
|
22
|
+
input: GlazeColorValue | GlazeFromInput | GlazeColorInput,
|
|
23
|
+
config?: GlazeConfigOverride,
|
|
24
|
+
): GlazeColorToken;
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The first argument is the color, in one of four shapes told apart by structure. The optional second argument is a [config override](#config-overrides) and works with every shape.
|
|
28
|
+
|
|
29
|
+
| Shape | Recognized by | Example | Form |
|
|
30
|
+
| --- | --- | --- | --- |
|
|
31
|
+
| Bare string | A string | `'#26fcb2'`, `'oklch(0.85 0.18 152)'` | Value |
|
|
32
|
+
| Value object | Any other object | `{ h: 152, s: 0.95, l: 0.74 }`, `{ r: 38, g: 252, b: 178 }` | Value |
|
|
33
|
+
| `{ from, ...overrides }` | Has a `from` key | `{ from: '#1a1a2e', base: bg, contrast: 'AA' }` | Value |
|
|
34
|
+
| Structured | Has a `hue` key | `{ hue: 152, saturation: 95, tone: 74 }` | Structured |
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
// bare string
|
|
38
|
+
glaze.color('#26fcb2');
|
|
39
|
+
// OKHSL value object
|
|
40
|
+
glaze.color({ h: 152, s: 0.95, l: 0.74 });
|
|
41
|
+
// OKHST value object (tone axis)
|
|
42
|
+
glaze.color({ h: 152, s: 0.95, t: 0.7 });
|
|
43
|
+
// value + overrides
|
|
44
|
+
glaze.color({ from: '#1a1a2e', hue: '+20', contrast: 'AA' });
|
|
45
|
+
// structured, 0–100 scales
|
|
46
|
+
glaze.color({ hue: 152, saturation: 95, tone: 74 });
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The three **value** forms start from a literal color and take [`GlazeColorOverrides`](#glazecoloroverrides). The **structured** form is a theme-style definition on 0–100 scales; see [`GlazeColorInput`](#glazecolorinput).
|
|
50
|
+
|
|
51
|
+
## `GlazeColorValue`
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
type GlazeColorValue =
|
|
55
|
+
| string
|
|
56
|
+
| OkhslColor
|
|
57
|
+
| OkhstColor
|
|
58
|
+
| RgbColor
|
|
59
|
+
| OklchColor;
|
|
60
|
+
// An editor hint; strings are validated at runtime.
|
|
61
|
+
type HexColor = `#${string}`;
|
|
62
|
+
|
|
63
|
+
interface OkhslColor { h: number; s: number; l: number }
|
|
64
|
+
interface OkhstColor { h: number; s: number; t: number }
|
|
65
|
+
interface RgbColor { r: number; g: number; b: number }
|
|
66
|
+
interface OklchColor { l: number; c: number; h: number }
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The literal color forms. They are accepted by `glaze.color()`, by [`from`](./color-definitions.md#from) on a theme color, by a token's [`base`](#pairing-colors), and by `glaze.shadow()` / `glaze.glow()`.
|
|
70
|
+
|
|
71
|
+
| Form | Example | Notes |
|
|
72
|
+
| --- | --- | --- |
|
|
73
|
+
| Hex | `'#26fcb2'`, `'#abc'`, `'#26fcb2ff'`, `'#abcd'` | 3, 4, 6, or 8 digits after `#`. |
|
|
74
|
+
| `rgb()` / `rgba()` | `'rgb(38 252 178)'`, `'rgba(38, 252, 178, 0.8)'` | Channels 0–255 or percentages. |
|
|
75
|
+
| `hsl()` / `hsla()` | `'hsl(152 97% 57%)'` | Saturation and lightness as percentages. |
|
|
76
|
+
| `okhsl()` | `'okhsl(152 95% 74%)'` | OKHSL, Glaze's own output format. |
|
|
77
|
+
| `okhst()` | `'okhst(152 95% 70%)'` | OKHST: the third value is tone (`70%` = tone 70). Not a native CSS color; [Tasty](https://tasty.style) output can carry it. |
|
|
78
|
+
| `oklch()` | `'oklch(0.85 0.18 152)'` | L 0–1 (or a percentage), C (a percentage maps `100%` to 0.4), H in degrees. |
|
|
79
|
+
| `OkhslColor` | `{ h: 152, s: 0.95, l: 0.74 }` | `h` 0–360; `s`, `l` 0–1. |
|
|
80
|
+
| `OkhstColor` | `{ h: 152, s: 0.95, t: 0.7 }` | `h` 0–360; `s`, `t` 0–1. The `t` key tells it apart from OKHSL. |
|
|
81
|
+
| `RgbColor` | `{ r: 38, g: 252, b: 178 }` | sRGB, 0–255. |
|
|
82
|
+
| `OklchColor` | `{ l: 0.85, c: 0.18, h: 152 }` | Same meaning as `oklch()`: L and C 0–1, H in degrees. |
|
|
83
|
+
|
|
84
|
+
- Function names are case-insensitive. Components can be separated by spaces or commas.
|
|
85
|
+
- An alpha component (`#rrggbbaa`, `#rgba`, `/ 0.8`, or a fourth comma value) is dropped with `console.warn`: `glaze: alpha component dropped from "<input>" (standalone color has no opacity field).` Use [`opacity`](#glazecoloroverrides) instead.
|
|
86
|
+
- Value objects are recognized by their keys: `r`, `g`, `b` → RGB; `l`, `c`, `h` → OKLCh; `h`, `s`, `t` → OKHST; otherwise OKHSL.
|
|
87
|
+
- Not supported: named colors (`'red'`), relative color syntax, angle units other than bare degrees, and `[r, g, b]` tuples.
|
|
88
|
+
|
|
89
|
+
| Input | Error |
|
|
90
|
+
| --- | --- |
|
|
91
|
+
| `'red'` | `glaze: unsupported color string "red".` |
|
|
92
|
+
| `'#12'` | `glaze: invalid hex color "#12".` |
|
|
93
|
+
| `'rgb(38 252)'` | `glaze: expected 3 components in "rgb(38 252)".` |
|
|
94
|
+
| `{ h: 152, s: 95, l: 74 }` | `glaze.color: OkhslColor s/l must be in 0–1 range. Did you mean the structured form { hue, saturation, tone } (which uses 0–100)?` |
|
|
95
|
+
| `[38, 252, 178]` | `glaze.color: RGB tuple [r, g, b] is no longer supported — use { r, g, b } instead.` |
|
|
96
|
+
| `{ r: 300, g: 0, b: 0 }` | `glaze.color: RgbColor r must be a finite number in 0–255 (got 300).` |
|
|
97
|
+
|
|
98
|
+
The OKHSL, OKHST, and OKLCh object checks reject `s`, `l`, `t`, or `c` above 1.5, which catches 0–100 input meant for the structured form.
|
|
99
|
+
|
|
100
|
+
## `GlazeColorOverrides`
|
|
101
|
+
|
|
102
|
+
The fields a value form can carry next to its color. `GlazeFromInput` is `GlazeColorOverrides` plus a required `from: GlazeColorValue`; a bare string or value object is the same with no overrides.
|
|
103
|
+
|
|
104
|
+
| Field | Type | Default | Description |
|
|
105
|
+
| --- | --- | --- | --- |
|
|
106
|
+
| `hue` | `number \| RelativeValue` | The value's hue | Absolute (0–360) replaces the hue. `'+N'` / `'-N'` is relative to the value's own hue, never to `base`. |
|
|
107
|
+
| `saturation` | `number` | The value's saturation | Saturation seed, 0–100. Replaces the value's saturation. |
|
|
108
|
+
| `saturationFactor` | `number` | `1` | Multiplier (0–1) on the saturation seed. |
|
|
109
|
+
| `tone` | `HCPair<ToneValue>` | The value's tone | Absolute (0–100), a tone delta (`'+N'` / `'-N'`), or `'max'` / `'min'`. A delta is measured from the value's own tone, or, with `base`, from the base. Under `mode: 'auto'` the dark variants mirror it; see [Pairing colors](#pairing-colors). |
|
|
110
|
+
| `darkHue` | `number \| RelativeValue` | `hue` | Hue for the dark variants. Absolute, or relative to the seed hue (the value's hue, or an absolute `hue` override). |
|
|
111
|
+
| `darkSaturation` | `number` | `saturation` | Dark saturation seed, 0–100. When set, `darkDesaturation` is not applied on top. |
|
|
112
|
+
| `darkSaturationFactor` | `number` | The light factor | Dark multiplier (0–1) on the dark seed. When set, `darkDesaturation` is not applied on top. |
|
|
113
|
+
| `mode` | `AdaptationMode` | `'auto'` | `'fixed'` maps without inverting; `'static'` keeps the same tone in every variant. See [`mode`](./color-definitions.md#mode). |
|
|
114
|
+
| `autoFlip` | `boolean` | The config's `autoFlip` | Mirror out-of-range results instead of clamping. See [`autoFlip`](./color-definitions.md#autoflip). |
|
|
115
|
+
| `contrast` | `HCPair<ContrastSpec>` | — | Contrast floor. Without `base`, measured against the input value itself; with `base`, against the base in each scheme. See [Contrast and high contrast](./contrast.md#contrastspec). |
|
|
116
|
+
| `base` | `GlazeColorToken \| GlazeColorValue` | — | The color this token's contrast and tone delta are measured against. See [Pairing colors](#pairing-colors). |
|
|
117
|
+
| `opacity` | `number` | `1` | Fixed alpha, 0–1. Out of range throws. With `contrast`, emits a `console.warn`. |
|
|
118
|
+
| `pastel` | `boolean` | The config override's `pastel` (`false`) | See [Per-color `pastel`](./color-definitions.md#per-color-pastel). |
|
|
119
|
+
| `role` | `RoleInput` | Inferred | Fixes APCA polarity. See [Roles on tokens](#roles-on-tokens). |
|
|
120
|
+
| `name` | `string` | — | Debug label. See [`name`](#name). |
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
// hue 282.9 → 302.9
|
|
124
|
+
glaze.color({ from: '#1a1a2e', hue: '+20' });
|
|
125
|
+
// saturation 0.375 → 0.5
|
|
126
|
+
glaze.color({ from: '#1a1a2e', saturation: 50 });
|
|
127
|
+
// saturation 0.375 → 0.187
|
|
128
|
+
glaze.color({ from: '#1a1a2e', saturationFactor: 0.5 });
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## `GlazeColorInput`
|
|
132
|
+
|
|
133
|
+
The structured form: the same definition a theme color would have, with its own seed. Hue is in degrees; saturation and tone are on 0–100.
|
|
134
|
+
|
|
135
|
+
| Field | Type | Default | Description |
|
|
136
|
+
| --- | --- | --- | --- |
|
|
137
|
+
| `hue` | `number` | — | Hue seed, 0–360. Required. |
|
|
138
|
+
| `saturation` | `number` | — | Saturation seed, 0–100. Required. |
|
|
139
|
+
| `tone` | `HCPair<number \| ExtremeValue>` | — | Tone 0–100, or `'max'` / `'min'`. Required. Tone deltas aren't accepted. |
|
|
140
|
+
| `saturationFactor` | `number` | `1` | Multiplier (0–1) on the seed. |
|
|
141
|
+
| `darkHue` | `number \| RelativeValue` | `hue` | Dark hue: absolute, or relative to `hue`. |
|
|
142
|
+
| `darkSaturation` | `number` | `saturation` | Dark saturation seed, 0–100. |
|
|
143
|
+
| `darkSaturationFactor` | `number` | The light factor | Dark multiplier (0–1) on the dark seed. |
|
|
144
|
+
| `mode` | `AdaptationMode` | `'auto'` | Adaptation mode. |
|
|
145
|
+
| `autoFlip` | `boolean` | The config's `autoFlip` | Mirror out-of-range results instead of clamping. |
|
|
146
|
+
| `opacity` | `number` | `1` | Fixed alpha, 0–1. |
|
|
147
|
+
| `base` | `GlazeColorToken \| GlazeColorValue` | — | See [Pairing colors](#pairing-colors). |
|
|
148
|
+
| `contrast` | `HCPair<ContrastSpec>` | — | Without `base`, measured against the token's own input color (`hue`, `saturation`, normal `tone`). |
|
|
149
|
+
| `pastel` | `boolean` | The config override's `pastel` (`false`) | Per-color `pastel`. |
|
|
150
|
+
| `role` | `RoleInput` | Inferred | Fixes APCA polarity. |
|
|
151
|
+
| `name` | `string` | — | Debug label. |
|
|
152
|
+
|
|
153
|
+
Out-of-range numbers throw before anything resolves, for example `glaze.color: structured saturation must be a finite number in 0–100 (got 120).` or `glaze.color: structured tone must be a finite number in 0–100 or 'max'/'min' (got +5).` `hue` and `darkHue` must be finite; the saturation seeds 0–100; the factors and `opacity` 0–1.
|
|
154
|
+
|
|
155
|
+
## Defaults
|
|
156
|
+
|
|
157
|
+
- Every form defaults to `mode: 'auto'`, so a token adapts between light and dark like a theme color.
|
|
158
|
+
- **Value forms** (bare strings, value objects, and `{ from, ... }`) keep the input tone exactly in the light variant: they resolve with a local `lightTone: false`. Pass `lightTone` in the config override to change that.
|
|
159
|
+
- **The structured form** uses the live global tone windows, like a theme color.
|
|
160
|
+
- Every other config field follows the live global config at resolve time, unless the [config override](#config-overrides) sets it. `pastel` is instance-only: it comes from the override or the color's `pastel`, never from `glaze.configure()`.
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
const brand = glaze.color('#26fcb2');
|
|
164
|
+
brand.resolve().light; // renders exactly #26fcb2
|
|
165
|
+
brand.json();
|
|
166
|
+
// → {
|
|
167
|
+
// light: 'oklch(0.8789 0.1857 162.47)',
|
|
168
|
+
// dark: 'oklch(0.3446 0.066 162.47)',
|
|
169
|
+
// }
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Token methods
|
|
173
|
+
|
|
174
|
+
| Method | Returns | Description |
|
|
175
|
+
| --- | --- | --- |
|
|
176
|
+
| `token.resolve()` | `ResolvedColor` | The four variants. `name` is the [`name`](#name), or `'value'` without one. |
|
|
177
|
+
| `token.token(options?)` | `Record<string, string>` | One color's state map, keyed by state alias: `''`, the dark state, the high-contrast state, and the two combined. Options: `format`, `modes`, `states`. |
|
|
178
|
+
| `token.tasty(options?)` | `Record<string, string>` | The same map as `token.token()`, for [Tasty](https://tasty.style). |
|
|
179
|
+
| `token.json(options?)` | `Record<string, string>` | `{ light, dark, lightContrast?, darkContrast? }`. Options: `format`, `modes`. |
|
|
180
|
+
| `token.css(options)` | `GlazeCssResult` | CSS custom property declarations, one string per variant. Always all four. Options: [`GlazeColorCssOptions`](#glazecolorcssoptions). |
|
|
181
|
+
| `token.dtcg(options?)` | `GlazeColorDtcgResult` | One W3C DTCG token (`{ $type: 'color', $value }`) per variant, under `light`, `dark?`, `lightContrast?`, `darkContrast?`. Options: `colorSpace` (`'srgb'` default, or `'oklch'`), `modes`. |
|
|
182
|
+
| `token.dtcgResolver(options)` | `GlazeDtcgResolverDocument` | One DTCG Resolver-Module document with the color under `name`. Options: `name` (required) plus the [`theme.dtcgResolver()` options](./output.md#dtcgresolveroptions). |
|
|
183
|
+
| `token.tailwind(options)` | `string` | A Tailwind v4 `@theme` block for `--color-<name>` plus dark and high-contrast overrides. Options: `name` (required) plus the [`theme.tailwind()` options](./output.md#tailwindoptions). |
|
|
184
|
+
| `token.export(override?)` | `GlazeColorTokenExport` | JSON-safe authoring snapshot. See [Snapshots](#tokenexportoverride--glazecolorfromdata). |
|
|
185
|
+
|
|
186
|
+
```ts
|
|
187
|
+
const brand = glaze.color('#26fcb2');
|
|
188
|
+
|
|
189
|
+
brand.token();
|
|
190
|
+
// → {
|
|
191
|
+
// '': 'oklch(0.8789 0.1857 162.47)',
|
|
192
|
+
// '@media(prefers-color-scheme: dark)': 'oklch(0.3446 0.066 162.47)',
|
|
193
|
+
// }
|
|
194
|
+
|
|
195
|
+
brand.css({ name: 'brand' }).light;
|
|
196
|
+
// → '--brand-color: oklch(0.8789 0.1857 162.47);'
|
|
197
|
+
|
|
198
|
+
brand.tailwind({ name: 'brand' });
|
|
199
|
+
// → '@theme {\n' +
|
|
200
|
+
// ' --color-brand: oklch(0.8789 0.1857 162.47);\n' +
|
|
201
|
+
// '}\n\n.dark {\n' +
|
|
202
|
+
// ' --color-brand: oklch(0.3446 0.066 162.47);\n' +
|
|
203
|
+
// '}'
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
- Every output that takes `format` defaults to `'oklch'`; `dtcg()` and `dtcgResolver()` take a `colorSpace` instead (`'srgb'` by default). `json()`, `css()`, and `tailwind()` accept only the native CSS formats (`'oklch'`, `'rgb'`, `'hsl'`) and throw on `'okhsl'` / `'okhst'`; `token()` and `tasty()` accept all five. See [Color formats](./output.md#color-formats).
|
|
207
|
+
- `modes` and `states` default to the global config, as on themes. `css()` ignores `modes`. See [Output modes](./output.md#output-modes).
|
|
208
|
+
- The output methods share their option types with themes; full shapes are in [Output formats](./output.md).
|
|
209
|
+
|
|
210
|
+
### `GlazeColorCssOptions`
|
|
211
|
+
|
|
212
|
+
| Field | Type | Default | Description |
|
|
213
|
+
| --- | --- | --- | --- |
|
|
214
|
+
| `name` | `string` | — | Required. The custom property name without `--`: `'brand'` gives `--brand-color`. |
|
|
215
|
+
| `format` | `GlazeColorFormat` | `'oklch'` | Native formats only. |
|
|
216
|
+
| `suffix` | `string` | `'-color'` | Appended to `name`, matching `theme.css()`. |
|
|
217
|
+
| `splitHue` | `boolean` | `false` | Emit the hue as a separate `--<name>-hue` property referenced with `var()`. Applies only with `format: 'oklch'`; other formats ignore it and emit the color inline. Throws when the token isn't pastel. See [`splitHue`](./output.md#hue-channel-splitting-splithue). |
|
|
218
|
+
|
|
219
|
+
```ts
|
|
220
|
+
const brand = glaze.color('#26fcb2', { pastel: true });
|
|
221
|
+
brand.css({ name: 'brand', splitHue: true }).light;
|
|
222
|
+
// → '--brand-hue: 162.4653546883244;\n' +
|
|
223
|
+
// '--brand-color: oklch(0.8789 0.0572 var(--brand-hue));'
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
`GlazeColorTailwindOptions` is `GlazeTailwindOptions` plus a required `name`; `GlazeColorDtcgResolverOptions` is `GlazeDtcgResolverOptions` plus a required `name`.
|
|
227
|
+
|
|
228
|
+
## Config overrides
|
|
229
|
+
|
|
230
|
+
The second argument of `glaze.color()` is a `GlazeConfigOverride`, the same type a theme takes: the resolve-relevant subset of the global config plus the instance-only `pastel`. The fields are listed in [`GlazeConfigOverride`](./configuration.md#glazeconfigoverride). It is sparse: fields it sets are fixed for the token, and fields it omits follow the live global config at resolve time; the details are in [Merge rules](./configuration.md#merge-rules).
|
|
231
|
+
|
|
232
|
+
On a token specifically:
|
|
233
|
+
|
|
234
|
+
- Value forms resolve with a local `lightTone: false`; a `lightTone` in the override replaces it (see [Defaults](#defaults)).
|
|
235
|
+
- `shadowTuning` and `glowTuning` are accepted but have no effect: a token has no shadows.
|
|
236
|
+
- A `contrastLevel` moves only this token. On a token with a `base`, set it on both; see [Clearing it, exporting it, base links](./contrast.md#clearing-it-exporting-it-base-links).
|
|
237
|
+
- `modes` and `states` aren't part of the override. Pass them to the output methods or set them globally.
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
// Let black flip all the way to white in dark.
|
|
241
|
+
glaze.color('#000000', { lightTone: false, darkTone: [15, 100] });
|
|
242
|
+
// dark: #ffffff (with the default darkTone, #f1f1f1)
|
|
243
|
+
|
|
244
|
+
// The structured form takes an override too.
|
|
245
|
+
glaze.color(
|
|
246
|
+
{ hue: 152, saturation: 95, tone: 74 },
|
|
247
|
+
{ darkTone: false, pastel: true },
|
|
248
|
+
);
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Pairing colors
|
|
252
|
+
|
|
253
|
+
Set `base` to anchor a token to another token or a raw color value. `contrast` and a tone delta then measure against the base's resolved variant in each scheme instead of against the token's own input, so text keeps its floor against its background in light, dark, and high contrast.
|
|
254
|
+
|
|
255
|
+
```ts
|
|
256
|
+
const bg = glaze.color('#1a1a2e');
|
|
257
|
+
|
|
258
|
+
// AA against bg in every scheme.
|
|
259
|
+
const text = glaze.color({ from: '#ffffff', base: bg, contrast: 'AA' });
|
|
260
|
+
// light: #ffffff on #1a1a2e (17.06:1); dark: #222222 on #dadbec (11.58:1)
|
|
261
|
+
|
|
262
|
+
// 8 tones lighter than bg, except in darkContrast: bg sits at tone 93
|
|
263
|
+
// there, so autoFlip mirrors the step to 8 tones darker.
|
|
264
|
+
const border = glaze.color({
|
|
265
|
+
from: '#000000',
|
|
266
|
+
base: bg,
|
|
267
|
+
tone: '+8',
|
|
268
|
+
mode: 'fixed',
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// A raw value is wrapped with glaze.color(value), so this equals `text`.
|
|
272
|
+
const text2 = glaze.color({
|
|
273
|
+
from: '#ffffff',
|
|
274
|
+
base: '#1a1a2e',
|
|
275
|
+
contrast: 'AA',
|
|
276
|
+
});
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
With `base`:
|
|
280
|
+
|
|
281
|
+
- `contrast` is solved against the base's `light`, `dark`, `lightContrast`, and `darkContrast` variant in turn.
|
|
282
|
+
- A tone delta (`'+N'` / `'-N'`) is measured from the base, as for a theme's [dependent colors](./color-definitions.md#tone-values). In light and high contrast the step is exact. Under `mode: 'auto'` the dark variant applies the delta to the base's light tone, then inverts and remaps the result through the dark window, so the step flips direction and shrinks: an `'auto'` version of `border` above is 6.93 tones darker than `bg` in dark. `'fixed'` and `'static'` apply it to the base's tone in each scheme, sign kept.
|
|
283
|
+
- A relative `hue` still anchors to the token's own seed (the value passed to `glaze.color()`), not to the base.
|
|
284
|
+
- `mode` applies to the pair: `'auto'` mirrors the relationship in dark, `'fixed'` keeps its sign.
|
|
285
|
+
- The base is resolved lazily, when the dependent first resolves.
|
|
286
|
+
- **A structured base is linked at full range.** When a value-form token uses a structured token as its base, the anchor is the base's raw input tone, not its windowed output: a structured base at tone 5 renders at tone 9.7 in light, but a `tone: '+10'` dependent lands at 15. The base's own `resolve()` output is unchanged.
|
|
287
|
+
- An unreachable floor pins the token to the extreme of the side the solver searched first and emits a `console.warn`, such as `glaze: color "value" cannot meet WCAG 4.50 in dark scheme (got <ratio>). …`, deduplicated by color, scheme, target, and measured value. Set [`name`](#name) to make the warning identifiable.
|
|
288
|
+
|
|
289
|
+
Chains compose:
|
|
290
|
+
|
|
291
|
+
```ts
|
|
292
|
+
const page = glaze.color('#000000');
|
|
293
|
+
const surface = glaze.color({
|
|
294
|
+
from: '#222222',
|
|
295
|
+
base: page,
|
|
296
|
+
contrast: 'AAA',
|
|
297
|
+
});
|
|
298
|
+
const label = glaze.color({
|
|
299
|
+
from: '#ffffff',
|
|
300
|
+
base: surface,
|
|
301
|
+
contrast: 'AA',
|
|
302
|
+
});
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Roles on tokens
|
|
306
|
+
|
|
307
|
+
The [role](./color-definitions.md#roles) chain applies to tokens, with these inputs:
|
|
308
|
+
|
|
309
|
+
- An explicit `role` wins.
|
|
310
|
+
- With `inferRole` on, the [`name`](#name) is used for inference: `name: 'brand-bg'` resolves as a surface.
|
|
311
|
+
- With a `base` token, the base is treated as the background, so the token defaults to `'text'`.
|
|
312
|
+
- Without a `base`, a `contrast` floor or tone delta is measured against a hidden copy of the input color. That copy resolves as `'text'`, so the token defaults to `'surface'`.
|
|
313
|
+
|
|
314
|
+
APCA results depend on the role, so set `role` explicitly on any token with an `{ apca }` floor.
|
|
315
|
+
|
|
316
|
+
## `name`
|
|
317
|
+
|
|
318
|
+
`name` is a debug label. It appears in warnings and errors instead of `"value"`, and as `resolve().name`. It doesn't change output keys: `token()`, `tasty()`, and `json()` stay keyed by scheme, and the custom property name comes from `css({ name })`. It also feeds [role inference](#roles-on-tokens).
|
|
319
|
+
|
|
320
|
+
| Invalid `name` | Error |
|
|
321
|
+
| --- | --- |
|
|
322
|
+
| `''` or whitespace | ``glaze.color: name must be a non-empty string. Omit `name` if you do not want to set a debug label.`` |
|
|
323
|
+
| `'value'`, `'seed'`, `'externalBase'` | `glaze.color: name "value" is reserved (used internally). Reserved names are: "value", "seed", "externalBase". Pick a different name.` |
|
|
324
|
+
|
|
325
|
+
## `token.export(override?)` / `glaze.colorFrom(data)`
|
|
326
|
+
|
|
327
|
+
`token.export()` returns a JSON-safe authoring snapshot: the original input, the overrides (with a token `base` exported recursively), and the effective config frozen at call time. `glaze.colorFrom()` rebuilds the token from it and pins the frozen config, so later `glaze.configure()` calls don't change the restored token.
|
|
328
|
+
|
|
329
|
+
```ts
|
|
330
|
+
const bg = glaze.color('#1a1a2e');
|
|
331
|
+
const text = glaze.color({ from: '#ffffff', base: bg, contrast: 'AA' });
|
|
332
|
+
|
|
333
|
+
const data = text.export();
|
|
334
|
+
// → {
|
|
335
|
+
// kind: 'color',
|
|
336
|
+
// version: 2,
|
|
337
|
+
// form: 'value',
|
|
338
|
+
// input: '#ffffff',
|
|
339
|
+
// overrides: {
|
|
340
|
+
// contrast: 'AA',
|
|
341
|
+
// base: {
|
|
342
|
+
// kind: 'color',
|
|
343
|
+
// version: 2,
|
|
344
|
+
// form: 'value',
|
|
345
|
+
// input: '#1a1a2e',
|
|
346
|
+
// config: { … },
|
|
347
|
+
// },
|
|
348
|
+
// },
|
|
349
|
+
// config: {
|
|
350
|
+
// lightTone: false,
|
|
351
|
+
// darkTone: { lo: 15, hi: 95, eps: 0.05 },
|
|
352
|
+
// darkDesaturation: 0.1,
|
|
353
|
+
// autoFlip: true,
|
|
354
|
+
// pastel: false,
|
|
355
|
+
// inferRole: true,
|
|
356
|
+
// },
|
|
357
|
+
// }
|
|
358
|
+
|
|
359
|
+
const restored = glaze.colorFrom(JSON.parse(JSON.stringify(data)));
|
|
360
|
+
// restored.resolve() is identical to text.resolve()
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
- Both the value and the structured forms round-trip.
|
|
364
|
+
- `override` merges over the token's own override for the snapshot only, and is passed to a nested `base` export too.
|
|
365
|
+
- The config freeze follows the same rules as [`theme.export()`](./theme.md#themeexportoverride): `contrastLevel` is frozen only when set on the token or passed as `override`; `modes` and `states` never are.
|
|
366
|
+
|
|
367
|
+
`GlazeColorTokenExport`:
|
|
368
|
+
|
|
369
|
+
| Field | Type | Description |
|
|
370
|
+
| --- | --- | --- |
|
|
371
|
+
| `kind` | `'color'` | Always written; optional on legacy snapshots. |
|
|
372
|
+
| `version` | `number` | `GLAZE_EXPORT_VERSION` (`2`). Always written; optional on legacy snapshots. |
|
|
373
|
+
| `form` | `'value' \| 'structured'` | Which input shape created the token. |
|
|
374
|
+
| `input` | `GlazeColorValue \| GlazeColorInputExport` | The raw value (`form: 'value'`) or the structured input. |
|
|
375
|
+
| `overrides` | `GlazeColorOverridesExport` | The value form's overrides, when any were given. |
|
|
376
|
+
| `config` | `GlazeConfigOverride` | The frozen effective config. |
|
|
377
|
+
|
|
378
|
+
`GlazeColorInputExport` and `GlazeColorOverridesExport` have the same fields as `GlazeColorInput` and `GlazeColorOverrides`, except that a token `base` is stored as its `GlazeColorTokenExport` (a raw-value base is kept as is).
|
|
379
|
+
|
|
380
|
+
`glaze.colorFrom()` throws when:
|
|
381
|
+
|
|
382
|
+
| Condition | Error |
|
|
383
|
+
| --- | --- |
|
|
384
|
+
| `data` isn't an object | `glaze.colorFrom: expected an object from token.export(), got <type>.` |
|
|
385
|
+
| `kind` is present and not `'color'` | `glaze.colorFrom: expected kind "color", got "<kind>".` |
|
|
386
|
+
| `version` is invalid or newer than the library | See [Restore checks](./index.md#restore-checks). |
|
|
387
|
+
| `form` isn't `'value'` or `'structured'` | `glaze.colorFrom: invalid "form" field — expected "value" or "structured" (got <form>).` |
|
|
388
|
+
| `input` is missing | `glaze.colorFrom: missing "input" field — expected the original GlazeColorValue.` (or `GlazeColorInput`) |
|
|
389
|
+
|
|
390
|
+
`glaze.isColorTokenExport(data)` tells a token snapshot apart from theme and palette snapshots; see [Type guards](./index.md#type-guards).
|