@tenphi/glaze 0.19.0 → 1.1.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/README.md +85 -31
- package/dist/index.cjs +391 -233
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +99 -35
- package/dist/index.d.mts +99 -35
- package/dist/index.mjs +388 -234
- package/dist/index.mjs.map +1 -1
- package/docs/api.md +589 -413
- package/docs/methodology.md +125 -50
- package/docs/migration.md +81 -144
- package/docs/okhst.md +116 -254
- package/package.json +1 -1
package/docs/migration.md
CHANGED
|
@@ -1,124 +1,44 @@
|
|
|
1
1
|
# Migration & Integration
|
|
2
2
|
|
|
3
|
-
How to
|
|
3
|
+
How to move an existing CSS, design-token, or application color system to
|
|
4
|
+
Glaze, then export the resulting palette in the shape your app and design tools
|
|
5
|
+
consume.
|
|
4
6
|
|
|
5
|
-
If you're starting from scratch, see [methodology.md](methodology.md) first — that's about
|
|
7
|
+
If you're starting from scratch, see [methodology.md](methodology.md) first — that's about _designing_ the palette. This doc is about _consuming_ it.
|
|
6
8
|
|
|
7
|
-
##
|
|
9
|
+
## Contents
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
- [Choosing an export](#choosing-an-export)
|
|
12
|
+
- [Wiring exports into the app](#wiring-exports-into-the-app)
|
|
13
|
+
- [Prefix map strategies](#prefix-map-strategies)
|
|
14
|
+
- [Migrating an existing color system](#migrating-from-an-existing-color-system)
|
|
15
|
+
- [Common pitfalls](#common-pitfalls)
|
|
10
16
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
`lightness` is gone. Replace it with `tone` everywhere:
|
|
14
|
-
|
|
15
|
-
```ts
|
|
16
|
-
// before
|
|
17
|
-
theme.colors({ surface: { lightness: 97 }, text: { base: 'surface', lightness: '-52' } });
|
|
18
|
-
// after
|
|
19
|
-
theme.colors({ surface: { tone: 97 }, text: { base: 'surface', tone: '-52' } });
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
The same applies to `glaze.color({ ..., lightness })` (structured form) → `tone`, and the `{ h, s, l }` value object is unchanged (still OKHSL) — but you can now also pass `{ h, s, t }` (OKHST) or an `okhst(H S% T%)` string.
|
|
23
|
-
|
|
24
|
-
Tone is **0–100** like the old lightness, but the *scale is contrast-uniform*, not perceptual-lightness-uniform. Numeric values won't land at the same OKHSL lightness — equal tone steps now give equal WCAG contrast. Re-eyeball absolute values (especially mid-range ones); relative deltas and contrast-floored tokens usually need no change because they were already contrast-driven.
|
|
25
|
-
|
|
26
|
-
### Config window shape
|
|
27
|
-
|
|
28
|
-
The lightness windows became tone windows, and `darkCurve` was removed (no curve to tune). The `[lo, hi]` tuple form carries over directly:
|
|
29
|
-
|
|
30
|
-
```ts
|
|
31
|
-
// before
|
|
32
|
-
glaze.configure({ lightLightness: [10, 100], darkLightness: [15, 95], darkCurve: 0.5 });
|
|
33
|
-
// after
|
|
34
|
-
glaze.configure({
|
|
35
|
-
lightTone: [10, 100],
|
|
36
|
-
darkTone: [15, 95],
|
|
37
|
-
// darkCurve removed
|
|
38
|
-
});
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
`lightLightness`/`darkLightness` → `lightTone`/`darkTone`. The window value is `[lo, hi]` (reference eps — the common form), `{ lo, hi, eps }` (advanced: explicit render curvature), or `false` to disable clamping. `false` removes the *boundaries* (full `[0, 100]` range), not the contrast-uniform tone curve. Per-token `glaze.color(value, config)` overrides use the same shape.
|
|
42
|
-
|
|
43
|
-
### The `contrast` prop now selects a metric
|
|
44
|
-
|
|
45
|
-
A bare number or preset is still **WCAG** and needs no change. To use APCA or split a pair across the metric, use the object form:
|
|
46
|
-
|
|
47
|
-
```ts
|
|
48
|
-
contrast: 4.5 // unchanged — WCAG 4.5
|
|
49
|
-
contrast: { wcag: 6 } // explicit WCAG
|
|
50
|
-
contrast: { apca: 60 } // APCA Lc floor
|
|
51
|
-
contrast: { wcag: [4.5, 7] } // pair inside the metric
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### Forcing extremes: `'max'` / `'min'`
|
|
55
|
-
|
|
56
|
-
For colors that should sit at the scheme's tone extreme (pure-white knockouts, near-black scrims, deliberately faint disabled chips), reach for `tone: 'max'` / `tone: 'min'` instead of a large absolute number or a low contrast floor standing in for "push it all the way". `'max'` resolves to author tone 100, `'min'` to 0, and both flow through scheme mapping (so they invert in dark under `mode: 'auto'`). No `base` required.
|
|
57
|
-
|
|
58
|
-
```ts
|
|
59
|
-
// before — low contrast as a proxy for "stay near the surface"
|
|
60
|
-
'disabled-text': { base: 'chip', tone: '+1', contrast: 1.51, mode: 'fixed' }
|
|
61
|
-
// after — say it directly with tone
|
|
62
|
-
'disabled-text': { base: 'chip', tone: '+18', saturation: 0.4, autoFlip: false }
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
### The `autoFlip` prop (previously `flip`)
|
|
66
|
-
|
|
67
|
-
The per-color configuration property `flip` has been renamed to `autoFlip` to align with the global `autoFlip` configuration option and to avoid confusion with dark-mode/scheme tone inversion (which is handled automatically by the system).
|
|
68
|
-
|
|
69
|
-
Relative `tone` offsets that overshoot `[0, 100]` now **mirror to the other side of the base by default** (controlled by the per-color `autoFlip`, which inherits the global `autoFlip`, default `true`). Previously such offsets always clamped to the boundary. If you relied on clamping — e.g. `tone: '+48'` to stack a color up to 100 — set `autoFlip: false` on that color (or `glaze.configure({ autoFlip: false })` globally) to restore the clamping behavior. `autoFlip` also governs the contrast solver's direction (its previous sole role).
|
|
70
|
-
|
|
71
|
-
### Resolved variants store tone
|
|
72
|
-
|
|
73
|
-
`ResolvedColorVariant` now exposes `t` (tone, 0–1) instead of `l`. If you read resolved internals, convert with `variantToOkhsl(variant).l`. Token/CSS/JSON output uses native formats by default (`oklch` for `tokens()` / `json()`, `okhsl` for `tasty()`); use `tasty({ format: 'okhsl' })` or `tasty({ format: 'okhst' })` for Glaze-native spaces.
|
|
74
|
-
|
|
75
|
-
### 0.16.0 — output format defaults and Tasty-only spaces
|
|
76
|
-
|
|
77
|
-
**Breaking changes in 0.16.0:**
|
|
78
|
-
|
|
79
|
-
| Export | Old default | New default |
|
|
80
|
-
|---|---|---|
|
|
81
|
-
| `tokens()` / `json()` (theme + palette) | `okhsl` | `oklch` |
|
|
82
|
-
| Standalone `.json()` | `okhsl` | `oklch` |
|
|
83
|
-
| `tasty()` | `okhsl` (unchanged) | `okhsl` |
|
|
84
|
-
|
|
85
|
-
`'okhsl'` and the new `'okhst'` output format are **Tasty-only**. Passing either to `css()`, `tailwind()`, `tokens()`, or `json()` throws. Migrate:
|
|
86
|
-
|
|
87
|
-
```ts
|
|
88
|
-
// before
|
|
89
|
-
theme.tokens({ format: 'okhsl' })
|
|
90
|
-
theme.json()
|
|
91
|
-
|
|
92
|
-
// after — pick one
|
|
93
|
-
theme.tasty({ format: 'okhsl' }) // Tasty #name keys, okhsl strings
|
|
94
|
-
theme.tokens({ format: 'oklch' }) // native CSS (new default)
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
**New:** `splitHue` on `css()` / `tasty()` (theme + palette) and standalone `color.css()` emits hue as a separate custom property for runtime re-skinning. Requires `format: 'oklch'` and every color to be pastel. See [api.md → Hue channel splitting](api.md#hue-channel-splitting-splithue).
|
|
98
|
-
|
|
99
|
-
### Export snapshots
|
|
17
|
+
## Choosing an export
|
|
100
18
|
|
|
101
|
-
|
|
19
|
+
Glaze has two layers of “export”:
|
|
102
20
|
|
|
103
|
-
|
|
21
|
+
1. **Authoring snapshots** — `theme.export(override?)` / `token.export(override?)` / `palette.export(override?)`, restored with `glaze.themeFrom` / `colorFrom` / `paletteFrom`. These carry definitions, relations, and a config freeze taken at export time (`kind` + `version`). Live instances keep only a sparse local override; omitted global fields track `configure()` until export.
|
|
22
|
+
2. **Resolved output** — the seven shapes below. These are color strings / design-token documents for apps and tools, **not** restorable as authoring config.
|
|
104
23
|
|
|
105
|
-
Glaze emits the same resolved colors in
|
|
24
|
+
Glaze emits the same resolved colors in seven shapes. Pick one based on your
|
|
25
|
+
renderer or tooling.
|
|
106
26
|
|
|
107
|
-
| Method
|
|
108
|
-
|
|
109
|
-
| `palette.tasty(options?)`
|
|
110
|
-
| `palette.tokens(options?)`
|
|
111
|
-
| `palette.css(options?)`
|
|
112
|
-
| `palette.json(options?)`
|
|
113
|
-
| `palette.dtcg(options?)`
|
|
114
|
-
| `palette.dtcgResolver(options?)` | `{ version, sets, modifiers, resolutionOrder }`
|
|
115
|
-
| `palette.tailwind(options?)`
|
|
27
|
+
| Method | Output shape | Use it for |
|
|
28
|
+
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
29
|
+
| `palette.tasty(options?)` | `{ '#name': { '': value, '@media(prefers-color-scheme: dark)': value, '@media(prefers-contrast: more)': value } }` | The [Tasty](https://tasty.style) style system. Single object, state aliases keyed inside each token. |
|
|
30
|
+
| `palette.tokens(options?)` | `{ light: { name: value }, dark: { name: value }, ... }` | Most CSS-in-JS systems. Per-variant flat maps, easy to feed into a `:root { ... }` selector via your framework's globals. |
|
|
31
|
+
| `palette.css(options?)` | `{ light: '--name-color: rgb(...);', dark: '...', ... }` | Framework-free CSS / static stylesheets. Variant-grouped CSS custom property strings ready to wrap in `:root` and `prefers-color-scheme` queries. |
|
|
32
|
+
| `palette.json(options?)` | `{ themeName: { name: { light, dark, ... } } }` | Tooling, JSON pipelines. |
|
|
33
|
+
| `palette.dtcg(options?)` | `{ light: { name: { $type, $value } }, dark: { ... }, ... }` | W3C [DTCG 2025.10](https://www.designtokens.org/) `.tokens.json` — Figma, Tokens Studio, Style Dictionary, Terrazzo, Penpot. One document per scheme. |
|
|
34
|
+
| `palette.dtcgResolver(options?)` | `{ version, sets, modifiers, resolutionOrder }` | W3C DTCG **Resolver-Module** — a single document describing every scheme variant as `sets` + a `scheme` modifier with a context per variant. For resolver tools such as Dispersa. |
|
|
35
|
+
| `palette.tailwind(options?)` | `'@theme { --color-*: ... } .dark { ... } ...'` | Tailwind CSS v4. A single ready-to-paste `@theme` block plus dark / high-contrast overrides. |
|
|
116
36
|
|
|
117
|
-
`tasty()`, `tokens()`, `json()`, `dtcg()`, `dtcgResolver()`, and `tailwind()` accept `modes` (`{ dark, highContrast }`). `css()` always returns all four strings (`light`, `dark`, `lightContrast`, `darkContrast`).
|
|
37
|
+
`tasty()`, `tokens()`, `json()`, `dtcg()`, `dtcgResolver()`, and `tailwind()` accept `modes` (`{ dark, highContrast }`). `css()` always returns all four strings (`light`, `dark`, `lightContrast`, `darkContrast`). CSS-string exports accept `format` and default to `'oklch'` (`tokens`/`json`/`css`/`tailwind`/`tasty`; `'rgb'` and `'hsl'` also available). `'okhsl'` and `'okhst'` are supported only on [Tasty](https://tasty.style)-shaped exports (`tasty()`, standalone `token()` / `.tasty()`). `dtcg()` and `dtcgResolver()` ignore `format` and use `colorSpace` instead (`'srgb'` default, `'oklch'` opt-in). See [api.md → Palette](api.md#palette) for full options.
|
|
118
38
|
|
|
119
39
|
## Wiring exports into the app
|
|
120
40
|
|
|
121
|
-
### Tasty
|
|
41
|
+
### [Tasty](https://tasty.style)
|
|
122
42
|
|
|
123
43
|
Spread the result of `palette.tasty()` into a global style call:
|
|
124
44
|
|
|
@@ -127,7 +47,9 @@ import type { Styles } from '@tenphi/tasty';
|
|
|
127
47
|
import { useGlobalStyles } from '@tenphi/tasty';
|
|
128
48
|
import { tastyStatic } from '@tenphi/tasty/static';
|
|
129
49
|
|
|
130
|
-
export const PALETTE_TOKENS = palette.tasty({
|
|
50
|
+
export const PALETTE_TOKENS = palette.tasty({
|
|
51
|
+
/* prefix map */
|
|
52
|
+
}) as Styles;
|
|
131
53
|
|
|
132
54
|
// In your root component:
|
|
133
55
|
useGlobalStyles('body', PALETTE_TOKENS);
|
|
@@ -136,9 +58,9 @@ useGlobalStyles('body', PALETTE_TOKENS);
|
|
|
136
58
|
tastyStatic('body', PALETTE_TOKENS);
|
|
137
59
|
```
|
|
138
60
|
|
|
139
|
-
By default the dark / high-contrast variants are keyed by media-query states — `'@media(prefers-color-scheme: dark)'` and `'@media(prefers-contrast: more)'` — so the tokens react to the OS preference out of the box, with no extra Tasty setup.
|
|
61
|
+
By default the dark / high-contrast variants are keyed by media-query states — `'@media(prefers-color-scheme: dark)'` and `'@media(prefers-contrast: more)'` — so the tokens react to the OS preference out of the box, with no extra [Tasty](https://tasty.style) setup.
|
|
140
62
|
|
|
141
|
-
If you'd rather drive the schemes from custom aliases (e.g. a manual toggle that also falls back to the OS preference), set your own [`glaze.configure({ states })`](api.md#configuration) and register what those states
|
|
63
|
+
If you'd rather drive the schemes from custom aliases (e.g. a manual toggle that also falls back to the OS preference), set your own [`glaze.configure({ states })`](api.md#configuration) and register what those states _mean_:
|
|
142
64
|
|
|
143
65
|
```ts
|
|
144
66
|
import { setGlobalPredefinedStates } from '@tenphi/tasty';
|
|
@@ -154,7 +76,7 @@ setGlobalPredefinedStates({
|
|
|
154
76
|
|
|
155
77
|
The state names here must match the `states` you set in [`glaze.configure({ states })`](api.md#configuration).
|
|
156
78
|
|
|
157
|
-
You can also register the tokens as a Tasty recipe instead of spreading them globally:
|
|
79
|
+
You can also register the tokens as a [Tasty](https://tasty.style) recipe instead of spreading them globally:
|
|
158
80
|
|
|
159
81
|
```ts
|
|
160
82
|
import { configure, tasty } from '@tenphi/tasty';
|
|
@@ -183,15 +105,15 @@ const stylesheet = `
|
|
|
183
105
|
Each property name is `--<prefix><name>-color` by default. Override the suffix:
|
|
184
106
|
|
|
185
107
|
```ts
|
|
186
|
-
palette.css({ suffix: '' });
|
|
187
|
-
palette.css({ format: 'oklch' });
|
|
108
|
+
palette.css({ suffix: '' }); // → '--surface: rgb(...);'
|
|
109
|
+
palette.css({ format: 'oklch' }); // → '--surface-color: oklch(...);'
|
|
188
110
|
```
|
|
189
111
|
|
|
190
112
|
### Framework-agnostic JSON
|
|
191
113
|
|
|
192
114
|
```ts
|
|
193
115
|
const data = palette.json();
|
|
194
|
-
// → { primary: { surface: { light: '
|
|
116
|
+
// → { primary: { surface: { light: 'oklch(...)', dark: 'oklch(...)' } }, ... }
|
|
195
117
|
```
|
|
196
118
|
|
|
197
119
|
Feed into your tooling pipeline. Each color is grouped by theme name, then by token name, then by variant — no prefix logic to undo.
|
|
@@ -238,10 +160,10 @@ The `--color-*` namespace makes every color available as `bg-*` / `text-*` / `bo
|
|
|
238
160
|
|
|
239
161
|
`palette.tokens()` / `tasty()` / `css()` / `dtcg()` / `tailwind()` accept a `prefix` option:
|
|
240
162
|
|
|
241
|
-
| Value
|
|
242
|
-
|
|
243
|
-
| `true` (default)
|
|
244
|
-
| `false`
|
|
163
|
+
| Value | Result |
|
|
164
|
+
| ------------------------ | -------------------------------------------------------------------------- |
|
|
165
|
+
| `true` (default) | Every theme prefixes its tokens with `<themeName>-`. |
|
|
166
|
+
| `false` | No prefixes. Colliding keys produce a `console.warn`; first-write wins. |
|
|
245
167
|
| `Record<string, string>` | Per-theme prefix overrides. Themes not listed fall back to `<themeName>-`. |
|
|
246
168
|
|
|
247
169
|
The most common production pattern: **default theme unprefixed, every other theme prefixed with its name**:
|
|
@@ -252,15 +174,20 @@ palette.tasty({
|
|
|
252
174
|
default: '',
|
|
253
175
|
primary: 'primary-',
|
|
254
176
|
success: 'success-',
|
|
255
|
-
danger:
|
|
177
|
+
danger: 'danger-',
|
|
256
178
|
warning: 'warning-',
|
|
257
|
-
note:
|
|
179
|
+
note: 'note-',
|
|
258
180
|
},
|
|
259
181
|
});
|
|
260
182
|
```
|
|
261
183
|
|
|
262
184
|
This makes neutral tokens consume as `#surface`, `#border`, `#disabled-surface` (no theme namespace) while status colors live under `#danger-surface`, `#success-accent-text`, etc.
|
|
263
185
|
|
|
186
|
+
This explicit map is usually clearer for a neutral `default` theme. The
|
|
187
|
+
separate `primary` palette option below solves a different namespace problem:
|
|
188
|
+
it duplicates one named theme without a prefix while retaining that theme's
|
|
189
|
+
prefixed tokens.
|
|
190
|
+
|
|
264
191
|
### Alias themes for legacy names
|
|
265
192
|
|
|
266
193
|
Two aliases for the same theme instance produce identical token values under different prefixes — useful when you want to support a legacy token name without duplicating definitions:
|
|
@@ -269,7 +196,7 @@ Two aliases for the same theme instance produce identical token values under dif
|
|
|
269
196
|
const palette = glaze.palette({
|
|
270
197
|
default: defaultTheme,
|
|
271
198
|
primary: primaryTheme,
|
|
272
|
-
purple:
|
|
199
|
+
purple: primaryTheme, // legacy alias — same theme, different prefix
|
|
273
200
|
// ...
|
|
274
201
|
});
|
|
275
202
|
|
|
@@ -277,7 +204,7 @@ palette.tasty({
|
|
|
277
204
|
prefix: {
|
|
278
205
|
default: '',
|
|
279
206
|
primary: 'primary-',
|
|
280
|
-
purple:
|
|
207
|
+
purple: 'purple-', // emits #purple-surface alongside #primary-surface
|
|
281
208
|
},
|
|
282
209
|
});
|
|
283
210
|
```
|
|
@@ -286,13 +213,10 @@ Both `#primary-surface` and `#purple-surface` resolve to the exact same color. D
|
|
|
286
213
|
|
|
287
214
|
### `primary` (the unprefixed alias)
|
|
288
215
|
|
|
289
|
-
`glaze.palette(themes, { primary })` and the per-export `primary` option duplicate one theme's tokens
|
|
216
|
+
`glaze.palette(themes, { primary })` and the per-export `primary` option duplicate one theme's tokens _without_ prefix on top of any prefix map. Equivalent to listing that theme twice with different prefixes; useful when the "primary" theme is conceptually distinct from `default` and you want both sets of unprefixed tokens.
|
|
290
217
|
|
|
291
218
|
```ts
|
|
292
|
-
const palette = glaze.palette(
|
|
293
|
-
{ brand, accent },
|
|
294
|
-
{ primary: 'brand' },
|
|
295
|
-
);
|
|
219
|
+
const palette = glaze.palette({ brand, accent }, { primary: 'brand' });
|
|
296
220
|
palette.tokens();
|
|
297
221
|
// → { light: { 'brand-surface': '...', 'surface': '...', 'accent-surface': '...' } }
|
|
298
222
|
```
|
|
@@ -316,16 +240,19 @@ Walk your current color system and bucket every token into one of these categori
|
|
|
316
240
|
- **Shadows / overlays** — elevation, scrims.
|
|
317
241
|
- **One-off colors** — syntax highlighting, charts, illustrations.
|
|
318
242
|
|
|
319
|
-
Each bucket maps cleanly to one of the patterns in [methodology.md](methodology.md). The bucket determines the
|
|
243
|
+
Each bucket maps cleanly to one of the patterns in [methodology.md](methodology.md). The bucket determines the _shape_ of the Glaze definition (root vs dependent, `mode: 'auto'` vs `'fixed'`, contrast-floor vs absolute tone), not the value.
|
|
320
244
|
|
|
321
245
|
### 2. Reproduce the existing values
|
|
322
246
|
|
|
323
|
-
Pick a Glaze definition shape that lands the new color
|
|
247
|
+
Pick a Glaze definition shape that lands the new color _close to_ the legacy hex in light mode. The methodology doc explains the shape per bucket; the API doc covers the levers (`tone`, `saturation`, `contrast`, `mode`, `hue`).
|
|
324
248
|
|
|
325
249
|
Two tactics that make matching easier:
|
|
326
250
|
|
|
327
251
|
- **Anchor strong text at the edge** instead of solving for `'AAA'`. The contrast solver stops at the floor (cr=7), which usually leaves text noticeably softer than a legacy hex like `#1a1a1a`. An absolute `tone: 2` (or wherever the legacy token sits) preserves the look.
|
|
328
|
-
- **
|
|
252
|
+
- **Match the metric the old system actually used.** Numeric WCAG ratios are
|
|
253
|
+
useful when reproducing a measured legacy ratio. For new content roles,
|
|
254
|
+
prefer the APCA presets from the methodology. For low-stakes spacing, use a
|
|
255
|
+
tone delta instead of inventing a contrast target.
|
|
329
256
|
|
|
330
257
|
### 3. Keep the old token names
|
|
331
258
|
|
|
@@ -336,9 +263,19 @@ Use a custom `prefix` map (and theme aliases if needed) so the names your compon
|
|
|
336
263
|
// New: define them in Glaze, map the prefix so they emit unchanged.
|
|
337
264
|
|
|
338
265
|
defaultTheme.colors({
|
|
339
|
-
dark:
|
|
340
|
-
'dark-02': {
|
|
341
|
-
|
|
266
|
+
dark: { base: 'surface', tone: 2, saturation: 0.475 },
|
|
267
|
+
'dark-02': {
|
|
268
|
+
base: 'surface',
|
|
269
|
+
tone: '-1',
|
|
270
|
+
saturation: 0.375,
|
|
271
|
+
contrast: [9, 11],
|
|
272
|
+
},
|
|
273
|
+
'dark-03': {
|
|
274
|
+
base: 'surface',
|
|
275
|
+
tone: '-1',
|
|
276
|
+
saturation: 0.24,
|
|
277
|
+
contrast: [4.5, 5.5],
|
|
278
|
+
},
|
|
342
279
|
});
|
|
343
280
|
|
|
344
281
|
palette.tasty({ prefix: { default: '' } });
|
|
@@ -351,7 +288,7 @@ Once consumers are off the legacy names, rename the Glaze tokens to match your c
|
|
|
351
288
|
|
|
352
289
|
Glaze gives you light/dark/HC for free, but only the light mode is matched against the legacy palette. Before promoting the migration:
|
|
353
290
|
|
|
354
|
-
- Spot-check every surface, text, accent, and disabled pair in dark mode. The tone inversion plus per-color `mode` choices may produce results that
|
|
291
|
+
- Spot-check every surface, text, accent, and disabled pair in dark mode. The tone inversion plus per-color `mode` choices may produce results that _look right_ in light but feel off in dark (typical fix: switch a brand color to `mode: 'fixed'`, or anchor a foreground to `surface` instead of the brand fill — see [methodology.md](methodology.md)).
|
|
355
292
|
- If the legacy system had no high-contrast mode, audit the HC variants Glaze emits. Anywhere the resolved cr is too low or the color blows out, add an HC pair (`tone: ['-7', '-20']`, `contrast: [4.5, 7]`, etc.).
|
|
356
293
|
- Run real screens, not just the token grid. The interaction of multiple Glaze tokens against each other (text on chip, hover bg vs. fill, disabled label on disabled chip) is where mismatches show up.
|
|
357
294
|
|
|
@@ -361,16 +298,16 @@ After migration, mark every default-only token (borders, shadows, disabled chip,
|
|
|
361
298
|
|
|
362
299
|
## Common pitfalls
|
|
363
300
|
|
|
364
|
-
| Symptom
|
|
365
|
-
|
|
366
|
-
| Disabled state stops looking disabled in dark mode.
|
|
367
|
-
| Brand color flips to its complement in dark mode.
|
|
368
|
-
| Brand text washes out against the dark surface.
|
|
369
|
-
| Tokens look right in light, broken in HC.
|
|
370
|
-
| A relative `tone` like `'+48'` lands on the
|
|
371
|
-
| `palette.tokens()` emits unexpected unprefixed names.
|
|
372
|
-
| `console.warn: token "foo" collides with theme "bar"`.
|
|
373
|
-
| `console.warn: color "X" cannot meet contrast`.
|
|
301
|
+
| Symptom | Cause | Fix |
|
|
302
|
+
| ------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
303
|
+
| Disabled state stops looking disabled in dark mode. | Alpha-tinted overlay on `surface-text` (which inverts), giving asymmetric perceived contrast. | Replace it with an adaptive color anchored to `surface`; use a tone delta for visual spacing or a contrast floor when required. See [Chips and disabled states](methodology.md#chips-and-disabled-states). |
|
|
304
|
+
| Brand color flips to its complement in dark mode. | Default `mode: 'auto'` inverts the tone. | Set `mode: 'fixed'` so the tone is remapped (not inverted). |
|
|
305
|
+
| Brand text washes out against the dark surface. | Foreground was anchored to `accent-surface` (the brand fill), so contrast was only enforced against that fill — not the actual surface. | Anchor `accent-text` etc. to `surface` with `mode: 'auto'`. |
|
|
306
|
+
| Tokens look right in light, broken in HC. | The HC pass bypasses the tone window — solver runs over the full `[0, 100]` range. | Add explicit `[normal, hc]` pairs to `tone` / `contrast` for the affected tokens. |
|
|
307
|
+
| A relative `tone` like `'+48'` lands on the _wrong_ (darker) side of its base. | Overshooting offsets now mirror to the other side of the base by default (`autoFlip` inherits `autoFlip`). | Set `autoFlip: false` on the color to clamp to the boundary instead, or use `tone: 'max'`/`'min'` to force the extreme. |
|
|
308
|
+
| `palette.tokens()` emits unexpected unprefixed names. | A `primary` was set on the palette (or per-call) and is duplicating the theme's tokens without prefix. | Pass `primary: false` to disable for that export, or rename `glaze.palette(themes, { primary })`. |
|
|
309
|
+
| `console.warn: token "foo" collides with theme "bar"`. | Two themes resolved to the same output key under your prefix config. | Adjust the prefix map so each token is unique, or accept the first-write-wins behavior. |
|
|
310
|
+
| `console.warn: color "X" cannot meet contrast`. | The requested contrast target is physically unreachable for the color's hue/saturation against its base. | Lower the floor, change the base, or accept the closest passing variant. Use the `name` override on standalone colors to make the warning identifiable. |
|
|
374
311
|
|
|
375
312
|
## See also
|
|
376
313
|
|