@tenphi/glaze 0.0.0-snapshot.c3d484c → 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 +42 -16
- package/README.md +41 -22
- package/dist/index.cjs +311 -139
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +122 -43
- package/dist/index.d.mts +122 -43
- package/dist/index.mjs +311 -139
- 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 +212 -285
- 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 +9 -1
- package/docs/api.md +0 -2021
- package/docs/methodology.md +0 -686
- package/docs/okhst.md +0 -207
package/docs/migration.md
CHANGED
|
@@ -1,263 +1,75 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
title: Migration and upgrading
|
|
3
|
+
description: Move an existing color system to Glaze without renaming tokens, and upgrade between major Glaze versions.
|
|
4
|
+
---
|
|
2
5
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
## Contents
|
|
10
|
-
|
|
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
|
-
- [Upgrading Glaze](#upgrading-glaze)
|
|
16
|
-
- [Common pitfalls](#common-pitfalls)
|
|
17
|
-
|
|
18
|
-
## Choosing an export
|
|
19
|
-
|
|
20
|
-
Glaze has two layers of “export”:
|
|
21
|
-
|
|
22
|
-
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.
|
|
23
|
-
2. **Resolved output** — the seven shapes below. These are color strings / design-token documents for apps and tools, **not** restorable as authoring config.
|
|
24
|
-
|
|
25
|
-
Glaze emits the same resolved colors in seven shapes. Pick one based on your
|
|
26
|
-
renderer or tooling.
|
|
27
|
-
|
|
28
|
-
| Method | Output shape | Use it for |
|
|
29
|
-
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
30
|
-
| `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. |
|
|
31
|
-
| `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. |
|
|
32
|
-
| `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. |
|
|
33
|
-
| `palette.json(options?)` | `{ themeName: { name: { light, dark, ... } } }` | Tooling, JSON pipelines. |
|
|
34
|
-
| `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. |
|
|
35
|
-
| `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. |
|
|
36
|
-
| `palette.tailwind(options?)` | `'@theme { --color-*: ... } .dark { ... } ...'` | Tailwind CSS v4. A single ready-to-paste `@theme` block plus dark / high-contrast overrides. |
|
|
37
|
-
|
|
38
|
-
`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.
|
|
39
|
-
|
|
40
|
-
## Wiring exports into the app
|
|
41
|
-
|
|
42
|
-
### [Tasty](https://tasty.style)
|
|
43
|
-
|
|
44
|
-
Spread the result of `palette.tasty()` into a global style call:
|
|
45
|
-
|
|
46
|
-
```ts
|
|
47
|
-
import type { Styles } from '@tenphi/tasty';
|
|
48
|
-
import { useGlobalStyles } from '@tenphi/tasty';
|
|
49
|
-
import { tastyStatic } from '@tenphi/tasty/static';
|
|
50
|
-
|
|
51
|
-
export const PALETTE_TOKENS = palette.tasty({
|
|
52
|
-
/* prefix map */
|
|
53
|
-
}) as Styles;
|
|
54
|
-
|
|
55
|
-
// In your root component:
|
|
56
|
-
useGlobalStyles('body', PALETTE_TOKENS);
|
|
57
|
-
|
|
58
|
-
// Or, for zero-runtime builds:
|
|
59
|
-
tastyStatic('body', PALETTE_TOKENS);
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
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.
|
|
63
|
-
|
|
64
|
-
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_:
|
|
65
|
-
|
|
66
|
-
```ts
|
|
67
|
-
import { setGlobalPredefinedStates } from '@tenphi/tasty';
|
|
68
|
-
|
|
69
|
-
// glaze.configure({ states: { dark: '@dark', highContrast: '@hc' } });
|
|
70
|
-
setGlobalPredefinedStates({
|
|
71
|
-
'@dark':
|
|
72
|
-
'@root(schema=dark) | (!@root(schema) & @media(prefers-color-scheme: dark))',
|
|
73
|
-
'@hc':
|
|
74
|
-
'@root(contrast=high) | (!@root(contrast) & @media(prefers-contrast: more))',
|
|
75
|
-
});
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
The state names here must match the `states` you set in [`glaze.configure({ states })`](api.md#configuration).
|
|
79
|
-
|
|
80
|
-
You can also register the tokens as a [Tasty](https://tasty.style) recipe instead of spreading them globally:
|
|
81
|
-
|
|
82
|
-
```ts
|
|
83
|
-
import { configure, tasty } from '@tenphi/tasty';
|
|
84
|
-
|
|
85
|
-
configure({ recipes: { 'theme-tokens': PALETTE_TOKENS } });
|
|
86
|
-
|
|
87
|
-
const Page = tasty({
|
|
88
|
-
styles: { recipe: 'theme-tokens', fill: '#surface', color: '#surface-text' },
|
|
89
|
-
});
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
### CSS custom properties
|
|
93
|
-
|
|
94
|
-
```ts
|
|
95
|
-
const css = palette.css();
|
|
96
|
-
const stylesheet = `
|
|
97
|
-
:root { ${css.light} }
|
|
98
|
-
@media (prefers-color-scheme: dark) { :root { ${css.dark} } }
|
|
99
|
-
@media (prefers-contrast: more) { :root { ${css.lightContrast} } }
|
|
100
|
-
@media (prefers-color-scheme: dark) and (prefers-contrast: more) {
|
|
101
|
-
:root { ${css.darkContrast} }
|
|
102
|
-
}
|
|
103
|
-
`;
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
Each property name is `--<prefix><name>-color` by default. Override the suffix:
|
|
107
|
-
|
|
108
|
-
```ts
|
|
109
|
-
palette.css({ suffix: '' }); // → '--surface: rgb(...);'
|
|
110
|
-
palette.css({ format: 'oklch' }); // → '--surface-color: oklch(...);'
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
### Framework-agnostic JSON
|
|
114
|
-
|
|
115
|
-
```ts
|
|
116
|
-
const data = palette.json();
|
|
117
|
-
// → { primary: { surface: { light: 'oklch(...)', dark: 'oklch(...)' } }, ... }
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
Feed into your tooling pipeline. Each color is grouped by theme name, then by token name, then by variant — no prefix logic to undo.
|
|
121
|
-
|
|
122
|
-
### W3C DTCG (`.tokens.json`)
|
|
123
|
-
|
|
124
|
-
```ts
|
|
125
|
-
import { writeFileSync } from 'node:fs';
|
|
126
|
-
const dtcg = palette.dtcg();
|
|
127
|
-
|
|
128
|
-
writeFileSync('tokens.light.tokens.json', JSON.stringify(dtcg.light, null, 2));
|
|
129
|
-
if (dtcg.dark) {
|
|
130
|
-
writeFileSync('tokens.dark.tokens.json', JSON.stringify(dtcg.dark, null, 2));
|
|
131
|
-
}
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
Each document is a spec-conformant token tree. One file per scheme is the most tool-compatible convention — Style Dictionary treats them as themes, Tokens Studio as sets, and Figma as variable modes. Use `colorSpace: 'oklch'` for wide-gamut, Glaze-native values (no `hex`); the default `'srgb'` emits components plus a `hex` hint that every reader understands. Feed the files straight into Style Dictionary v4+, Tokens Studio, or any DTCG-compatible tool — no Glaze-specific transform needed.
|
|
135
|
-
|
|
136
|
-
### W3C DTCG Resolver-Module (single document)
|
|
137
|
-
|
|
138
|
-
When you want **one file** describing every scheme variant — for a resolver tool such as [Dispersa](https://github.com/dispersa-core/dispersa) — use `dtcgResolver()` instead of `dtcg()`:
|
|
139
|
-
|
|
140
|
-
```ts
|
|
141
|
-
import { writeFileSync } from 'node:fs';
|
|
142
|
-
const resolver = palette.dtcgResolver({ modes: { highContrast: true } });
|
|
143
|
-
|
|
144
|
-
writeFileSync('resolver.json', JSON.stringify(resolver, null, 2));
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
The document places the light tokens in `sets.base.sources[0]` (the default context) and emits a single `scheme` modifier with a context per variant (`light` / `dark` / `lightContrast` / `darkContrast`). Each non-default context holds that variant's exact resolved tokens — Glaze resolves `darkContrast` independently, so the four-context shape keeps every value correct (two independent modifiers would compose additively and produce wrong dark + high-contrast values). Rename the set, modifier, or contexts via `setName` / `modifierName` / `contextNames` if your resolver expects different labels. Prefer `dtcg()` when you need maximum per-file tool compatibility; prefer `dtcgResolver()` when a resolver tool consumes the document for you.
|
|
148
|
-
|
|
149
|
-
### Tailwind CSS v4
|
|
150
|
-
|
|
151
|
-
```ts
|
|
152
|
-
const css = palette.tailwind();
|
|
153
|
-
// Write to your CSS entry, or paste into a `@import "tailwindcss"` stylesheet:
|
|
154
|
-
// @theme { --color-primary-surface: oklch(...); ... }
|
|
155
|
-
// .dark { --color-primary-surface: oklch(...); ... }
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
The `--color-*` namespace makes every color available as `bg-*` / `text-*` / `border-*`. Drive dark mode from the OS preference instead of a class with `darkSelector: '@media (prefers-color-scheme: dark)'` (it nests `:root` automatically). High-contrast overrides land under `.high-contrast` and `.dark.high-contrast` by default; both are omitted unless `modes.highContrast` is enabled.
|
|
159
|
-
|
|
160
|
-
## Prefix map strategies
|
|
161
|
-
|
|
162
|
-
`palette.tokens()` / `tasty()` / `css()` / `dtcg()` / `tailwind()` accept a `prefix` option:
|
|
163
|
-
|
|
164
|
-
| Value | Result |
|
|
165
|
-
| ------------------------ | -------------------------------------------------------------------------- |
|
|
166
|
-
| `true` (default) | Every theme prefixes its tokens with `<themeName>-`. |
|
|
167
|
-
| `false` | No prefixes. Colliding keys produce a `console.warn`; first-write wins. |
|
|
168
|
-
| `Record<string, string>` | Per-theme prefix overrides. Themes not listed fall back to `<themeName>-`. |
|
|
169
|
-
|
|
170
|
-
The most common production pattern: **default theme unprefixed, every other theme prefixed with its name**:
|
|
171
|
-
|
|
172
|
-
```ts
|
|
173
|
-
palette.tasty({
|
|
174
|
-
prefix: {
|
|
175
|
-
default: '',
|
|
176
|
-
primary: 'primary-',
|
|
177
|
-
success: 'success-',
|
|
178
|
-
danger: 'danger-',
|
|
179
|
-
warning: 'warning-',
|
|
180
|
-
note: 'note-',
|
|
181
|
-
},
|
|
182
|
-
});
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
This makes neutral tokens consume as `#surface`, `#border`, `#disabled-surface` (no theme namespace) while status colors live under `#danger-surface`, `#success-accent-text`, etc.
|
|
186
|
-
|
|
187
|
-
This explicit map is usually clearer for a neutral `default` theme. The
|
|
188
|
-
separate `primary` palette option below solves a different namespace problem:
|
|
189
|
-
it duplicates one named theme without a prefix while retaining that theme's
|
|
190
|
-
prefixed tokens.
|
|
191
|
-
|
|
192
|
-
### Alias themes for legacy names
|
|
193
|
-
|
|
194
|
-
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:
|
|
195
|
-
|
|
196
|
-
```ts
|
|
197
|
-
const palette = glaze.palette({
|
|
198
|
-
default: defaultTheme,
|
|
199
|
-
primary: primaryTheme,
|
|
200
|
-
purple: primaryTheme, // legacy alias — same theme, different prefix
|
|
201
|
-
// ...
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
palette.tasty({
|
|
205
|
-
prefix: {
|
|
206
|
-
default: '',
|
|
207
|
-
primary: 'primary-',
|
|
208
|
-
purple: 'purple-', // emits #purple-surface alongside #primary-surface
|
|
209
|
-
},
|
|
210
|
-
});
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
Both `#primary-surface` and `#purple-surface` resolve to the exact same color. Drop the alias when the legacy name is no longer referenced.
|
|
214
|
-
|
|
215
|
-
### `primary` (the unprefixed alias)
|
|
216
|
-
|
|
217
|
-
`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.
|
|
218
|
-
|
|
219
|
-
```ts
|
|
220
|
-
const palette = glaze.palette({ brand, accent }, { primary: 'brand' });
|
|
221
|
-
palette.tokens();
|
|
222
|
-
// → { light: { 'brand-surface': '...', 'surface': '...', 'accent-surface': '...' } }
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
Override per call: `palette.tokens({ primary: 'accent' })`, or disable with `palette.tokens({ primary: false })`.
|
|
6
|
+
This page covers two kinds of move: bringing an existing CSS, design-token, or
|
|
7
|
+
application color system over to Glaze, and upgrading Glaze itself across a
|
|
8
|
+
major version. For designing a palette from scratch, see
|
|
9
|
+
[Designing a palette](./methodology.mdx); for choosing an export shape and
|
|
10
|
+
wiring it into your app, see [Exporting and integration](./exporting.mdx).
|
|
226
11
|
|
|
227
12
|
## Migrating from an existing color system
|
|
228
13
|
|
|
229
|
-
The fastest path
|
|
14
|
+
The fastest path is **map first, design second**. Get every existing token name
|
|
15
|
+
producing a Glaze-resolved color (even a rough first pass), wire the new
|
|
16
|
+
palette in, then iterate on the resolved colors without touching component
|
|
17
|
+
code.
|
|
230
18
|
|
|
231
19
|
### 1. Inventory the existing tokens
|
|
232
20
|
|
|
233
|
-
Walk your current color system and
|
|
21
|
+
Walk your current color system and put every token into one of these buckets:
|
|
234
22
|
|
|
235
|
-
- **Surfaces
|
|
236
|
-
- **Surface text
|
|
237
|
-
- **Borders
|
|
238
|
-
- **Brand fills
|
|
239
|
-
- **Brand foregrounds
|
|
240
|
-
|
|
241
|
-
- **
|
|
242
|
-
- **
|
|
23
|
+
- **Surfaces**: backgrounds, panels, cards, banners.
|
|
24
|
+
- **Surface text**: body text, headings, captions, labels.
|
|
25
|
+
- **Borders, dividers, focus rings**: neutral structural lines.
|
|
26
|
+
- **Brand fills**: solid CTA backgrounds, badges, status banners.
|
|
27
|
+
- **Brand foregrounds**: link text, status text, icon colors on neutral
|
|
28
|
+
backgrounds.
|
|
29
|
+
- **Disabled**: disabled chip and its label.
|
|
30
|
+
- **Shadows and overlays**: elevation and state layers (`shadow` / `glow`),
|
|
31
|
+
and fixed-alpha overlays such as scrims (`opacity`).
|
|
32
|
+
- **One-off colors**: syntax highlighting, charts, illustrations.
|
|
243
33
|
|
|
244
|
-
Each bucket maps
|
|
34
|
+
Each bucket maps onto one of the patterns in
|
|
35
|
+
[Designing a palette](./methodology.mdx). The bucket decides the _shape_ of the
|
|
36
|
+
Glaze definition (root or dependent, `mode: 'auto'` or `'fixed'`, a contrast
|
|
37
|
+
floor or an absolute tone), not its value.
|
|
245
38
|
|
|
246
39
|
### 2. Reproduce the existing values
|
|
247
40
|
|
|
248
|
-
Pick a
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
41
|
+
Pick a definition shape that lands the new color _close to_ the legacy value in
|
|
42
|
+
light mode. [Designing a palette](./methodology.mdx) explains the shape per
|
|
43
|
+
bucket; the [Color definitions](./reference/color-definitions.md) reference
|
|
44
|
+
covers the levers (`tone`, `saturation`, `contrast`, `mode`, `hue`).
|
|
45
|
+
|
|
46
|
+
Three tactics make matching easier:
|
|
47
|
+
|
|
48
|
+
- **Anchor strong text at the edge** instead of solving for `'AAA'`. The
|
|
49
|
+
contrast solver stops at the floor, which usually leaves text noticeably
|
|
50
|
+
softer than the legacy value: on a white surface, a neutral
|
|
51
|
+
`contrast: 'AAA'` (7:1) solves to tone 36, while a legacy `#1a1a1a` sits at
|
|
52
|
+
tone 6.2 (17.4:1). An absolute `tone` preserves the look, but author it a
|
|
53
|
+
little below the measured value: the light
|
|
54
|
+
[tone window](./schemes.mdx#tone-windows) lifts authored tones (tone 0
|
|
55
|
+
renders at about 5, tone 2 at 6.9), so a neutral `tone: 1.3` renders
|
|
56
|
+
`#1a1a1a`. With `lightTone: false` on the theme, light tones render exactly
|
|
57
|
+
as authored.
|
|
253
58
|
- **Match the metric the old system actually used.** Numeric WCAG ratios are
|
|
254
|
-
useful when
|
|
59
|
+
useful when you reproduce a measured legacy ratio. For new content roles,
|
|
255
60
|
prefer the APCA presets from the methodology. For low-stakes spacing, use a
|
|
256
|
-
tone delta instead of inventing a contrast target.
|
|
61
|
+
tone delta instead of inventing a contrast target. See
|
|
62
|
+
[Contrast solving](./contrast.mdx).
|
|
63
|
+
- **Pin values that are a contract with `from`.** A brand color you must honor
|
|
64
|
+
can be given as a literal: `{ from: '#2f5bff' }` reproduces that exact value
|
|
65
|
+
in the light scheme whatever the theme's seed saturation, while dark and high
|
|
66
|
+
contrast still adapt. Add `base` and `contrast` when it must stay legible
|
|
67
|
+
against something. See [`from`](./reference/color-definitions.md#from).
|
|
257
68
|
|
|
258
69
|
### 3. Keep the old token names
|
|
259
70
|
|
|
260
|
-
Use a custom `prefix` map (and
|
|
71
|
+
Use a custom `prefix` map (and [alias themes](./exporting.mdx#alias-themes-for-legacy-names)
|
|
72
|
+
if needed) so the names your components already consume keep working:
|
|
261
73
|
|
|
262
74
|
```ts
|
|
263
75
|
// Old: components consume `#dark`, `#dark-02`, `#dark-03`.
|
|
@@ -280,34 +92,132 @@ defaultTheme.colors({
|
|
|
280
92
|
});
|
|
281
93
|
|
|
282
94
|
palette.tasty({ prefix: { default: '' } });
|
|
283
|
-
// → '#dark', '#dark-02', '#dark-03'
|
|
95
|
+
// → '#dark', '#dark-02', '#dark-03': components don't change.
|
|
284
96
|
```
|
|
285
97
|
|
|
286
|
-
Once consumers are off the legacy names, rename the Glaze tokens to match your
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
98
|
+
Once consumers are off the legacy names, rename the Glaze tokens to match your
|
|
99
|
+
conventions. [Prefix map strategies](./exporting.mdx#prefix-map-strategies)
|
|
100
|
+
shows every naming option with real output.
|
|
101
|
+
|
|
102
|
+
### 4. Verify dark mode and high contrast before promoting
|
|
103
|
+
|
|
104
|
+
Glaze gives you light, dark, and high contrast for free, but only the light
|
|
105
|
+
scheme was matched against the legacy palette. Before you promote the
|
|
106
|
+
migration:
|
|
107
|
+
|
|
108
|
+
- Spot-check every surface, text, accent, and disabled pair in dark mode. The
|
|
109
|
+
tone inversion plus the per-color `mode` can produce results that look right
|
|
110
|
+
in light but feel off in dark. The usual fixes: switch a brand color to
|
|
111
|
+
`mode: 'fixed'`, or anchor a foreground to `surface` instead of the brand
|
|
112
|
+
fill (see [adaptation modes](./schemes.mdx#adaptation-modes) and
|
|
113
|
+
[Designing a palette](./methodology.mdx)).
|
|
114
|
+
- If the legacy system had no high-contrast mode, audit the high-contrast
|
|
115
|
+
variants Glaze emits. Wherever the resolved contrast is too low or a color
|
|
116
|
+
blows out, add a `[normal, highContrast]` pair (`tone: ['-7', '-20']`,
|
|
117
|
+
`contrast: [4.5, 7]`, and so on).
|
|
118
|
+
- Run real screens, not just the token grid. The interaction of several Glaze
|
|
119
|
+
tokens against each other (text on a chip, a hover background against its
|
|
120
|
+
fill, a disabled label on a disabled chip) is where mismatches show up.
|
|
295
121
|
|
|
296
122
|
### 5. Trim what `extend()` doesn't need
|
|
297
123
|
|
|
298
|
-
After migration, mark every default-only token (borders, shadows,
|
|
124
|
+
After the migration, mark every default-only token (borders, shadows, glows,
|
|
125
|
+
the disabled chip, code highlighting, and so on) `inherit: false`. Colored
|
|
126
|
+
sibling themes created with `extend()` only need the accent and tinted-surface
|
|
127
|
+
chain; flagging the rest cuts the token set each theme emits dramatically.
|
|
299
128
|
|
|
300
129
|
## Upgrading Glaze
|
|
301
130
|
|
|
131
|
+
### 3.0 — shadows and glows
|
|
132
|
+
|
|
133
|
+
`shadow` was rebuilt so `intensity` produces the same visible change on every
|
|
134
|
+
surface, and `glow` was added as its mirror. Both emit a translucent pigment
|
|
135
|
+
whose opacity is solved, per surface and per scheme, so the composite lands an
|
|
136
|
+
exact **tone step** away from `bg`: darker for `shadow`, lighter for `glow`.
|
|
137
|
+
See [Shadows and glows](./shadows-and-glows.mdx).
|
|
138
|
+
|
|
139
|
+
What changed:
|
|
140
|
+
|
|
141
|
+
- **`intensity` is a tone step.** In 2.x it went through a `tanh` curve in
|
|
142
|
+
OKHSL lightness, so the same number darkened white by 9 tones, a tone-35
|
|
143
|
+
surface by 13.9, and a tone-6 surface by 4.4. Now `intensity: 10` is 10 tones
|
|
144
|
+
on every surface with 10 tones of room. A step past black (or white, for a
|
|
145
|
+
glow) is pinned to the strongest composite the pigment reaches, and Glaze
|
|
146
|
+
warns; see [Unreachable steps](./shadows-and-glows.mdx#unreachable-steps).
|
|
147
|
+
- **`fg` is the object, not a contrast reference.** In 2.x, strength scaled
|
|
148
|
+
with `|l_bg − l_fg|` and the docs pointed `fg` at `text`, which ran opposite
|
|
149
|
+
to the physics: a white card on a light surface got almost no shadow. Now
|
|
150
|
+
`fg` is the element casting the shadow; a darker object deepens it, a lighter
|
|
151
|
+
one softens it, and an object at the surface's tone (or no `fg`) gives
|
|
152
|
+
exactly `intensity`.
|
|
153
|
+
- **The pigment is neutral by default**: pure black for `shadow`, pure white
|
|
154
|
+
for `glow`. The surface keeps its hue and the output is clean: `intensity: 10`
|
|
155
|
+
on a white surface emits `rgb(0 0 0 / 0.1326)`, which composites to tone 90.
|
|
156
|
+
- **The `ShadowTuning` fields were replaced.** `saturationFactor`,
|
|
157
|
+
`maxSaturation`, `lightnessFactor`, `lightnessBounds`, `minGapTarget`,
|
|
158
|
+
`alphaMax`, and `bgHueBlend` are gone. The new knobs are `objectInfluence`,
|
|
159
|
+
`tint`, `reach`, and `hueBleed`, shared by `shadowTuning` and the new
|
|
160
|
+
`glowTuning`. The type is now `LightingTuning`; `ShadowTuning` and
|
|
161
|
+
`GlowTuning` remain as aliases of it.
|
|
162
|
+
- **`GLAZE_EXPORT_VERSION` is 2.** A version-1 theme or palette snapshot that
|
|
163
|
+
contains shadow colors or a `shadowTuning` now throws on restore instead of
|
|
164
|
+
silently re-rendering every shadow.
|
|
165
|
+
- **`ColorDef` has a fourth member, `GlowColorDef`.** Exhaustive `switch`es
|
|
166
|
+
over `def.type` need a `'glow'` branch.
|
|
167
|
+
|
|
168
|
+
How 2.x shadows map onto 3.0 (2.x steps measured with browser compositing):
|
|
169
|
+
|
|
170
|
+
| 2.x definition | 2.x step for `intensity: 10` | 3.0 equivalent |
|
|
171
|
+
| --- | --- | --- |
|
|
172
|
+
| No `fg`, light surface (tone 97–100) | ≈ 9 tones | `intensity: 9`, or keep `10` (a tone stronger) |
|
|
173
|
+
| No `fg`, mid surface (tone 50) | ≈ 13.6 tones | `intensity: 13.6`, or settle on one value for all surfaces |
|
|
174
|
+
| No `fg`, dark surface (tone 10) | ≈ 7.6 tones, and saturating | `intensity: 7.6`; dark shadows were weaker than authored and now aren't |
|
|
175
|
+
| `fg: 'text'` on a light surface | ≈ 7 tones | Drop `fg` and use `intensity: 7`, or point `fg` at the element and re-tune |
|
|
176
|
+
| `fg` = a near-white card on light | ≈ 0.3 tones (barely visible) | `fg: 'card'`, then pick `intensity` for the look you want |
|
|
177
|
+
|
|
178
|
+
Then:
|
|
179
|
+
|
|
180
|
+
1. Replace `fg: 'text'` (or any contrast-reference `fg`) with either no `fg` or
|
|
181
|
+
the element that casts the shadow.
|
|
182
|
+
2. Pick `intensity` as the number of tones you want. For a ladder, `4` / `8` /
|
|
183
|
+
`12` reads as evenly spaced on every surface with room for the deepest step.
|
|
184
|
+
Check the dark schemes: a white `auto` page sits at about tone 9 in dark and
|
|
185
|
+
at tone 0 in dark high contrast. Shadows on it deeper than about `8`–`9` (a
|
|
186
|
+
saturated page has a little less room) pin to black and warn in dark, and
|
|
187
|
+
every shadow on it stays transparent and warns in dark high contrast. Use
|
|
188
|
+
glows for dark elevation, on a surface that also leaves them room in light
|
|
189
|
+
([Dark-mode elevation](./shadows-and-glows.mdx#dark-mode-elevation)). Leaving
|
|
190
|
+
a shadow token out of the dark schemes hides the pinned result but not the
|
|
191
|
+
warning: every color resolves in all four schemes.
|
|
192
|
+
3. Delete the old fields inside `tuning` / `shadowTuning` (`saturationFactor`,
|
|
193
|
+
`alphaMax`, …); TypeScript flags them. A tinted pigment is
|
|
194
|
+
`tuning: { tint: 0.3, reach: 0.85 }`. There is no opacity cap: lower
|
|
195
|
+
`intensity` for a lighter overlay.
|
|
196
|
+
4. Replace hand-tuned translucent white mixes (dark-mode elevation, edge
|
|
197
|
+
highlights, lightening state layers) with
|
|
198
|
+
`{ type: 'glow', bg, intensity }`. Direction is fixed per type, so a
|
|
199
|
+
`shadow` darkens and a `glow` lightens in light and dark alike. A glow on a
|
|
200
|
+
white light-mode surface has nothing to lighten and warns.
|
|
201
|
+
5. Re-export snapshots. A 2.x snapshot with shadows or a `shadowTuning` throws
|
|
202
|
+
on restore, so re-author it. In code, rebuild the theme with 3.0 shadow defs
|
|
203
|
+
and call `export()`. In the JSON, fix the shadow defs, drop
|
|
204
|
+
`config.shadowTuning`, and set `version` to `2` (a palette snapshot has one at
|
|
205
|
+
the top and one per theme), then load it and export again. Snapshots without
|
|
206
|
+
shadows load unchanged.
|
|
207
|
+
|
|
208
|
+
`glaze.shadow()` keeps its signature with the new semantics, and `glaze.glow()`
|
|
209
|
+
takes the same input. See
|
|
210
|
+
[`glaze.shadow()` / `glaze.glow()`](./reference/utilities.md#glazeshadowinput--glazeglowinput).
|
|
211
|
+
|
|
302
212
|
### 2.0 — `format*` takes 0–1
|
|
303
213
|
|
|
304
214
|
The `format*` writers (`formatOkhsl`, `formatOkhst`, `formatRgb`, `formatHsl`,
|
|
305
|
-
`formatOklch`) used to take `s` / `l` / `t` on a 0–100 percentage scale while
|
|
306
|
-
everything that
|
|
307
|
-
`srgbToOkhsl`, `oklabToOkhsl`, `okhslToSrgb`
|
|
308
|
-
the two, which is the obvious thing to do, was off by 100x and failed
|
|
309
|
-
`0.7` is a legal percentage, so you got a valid CSS string naming the
|
|
310
|
-
(near-black) color.
|
|
215
|
+
`formatOklch`) used to take `s` / `l` / `t` on a 0–100 percentage scale, while
|
|
216
|
+
everything that _produces_ those values (`resolve()`, `variantToOkhsl`,
|
|
217
|
+
`srgbToOkhsl`, `oklabToOkhsl`, `okhslToSrgb`) returns them on 0–1. Composing
|
|
218
|
+
the two, which is the obvious thing to do, was off by 100x and failed
|
|
219
|
+
silently: `0.7` is a legal percentage, so you got a valid CSS string naming the
|
|
220
|
+
wrong (near-black) color.
|
|
311
221
|
|
|
312
222
|
The writers now take the 0–1 factors, so the whole library speaks one scale.
|
|
313
223
|
Drop the `* 100`:
|
|
@@ -321,31 +231,48 @@ Drop the `* 100`:
|
|
|
321
231
|
```
|
|
322
232
|
|
|
323
233
|
Nothing else moves: `h` was always 0–360, `alpha` was always 0–1, and every
|
|
324
|
-
export method (`css()
|
|
325
|
-
`tailwind()
|
|
326
|
-
internally. Dropping the redundant `×100 ÷100` round-trip shifts
|
|
327
|
-
an ULP, which shows up nowhere except the (meaningless) hue term
|
|
328
|
-
fully
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
`
|
|
234
|
+
export method (`css()`, `tokens()`, `json()`, `tasty()`, `dtcg()`,
|
|
235
|
+
`tailwind()`, `glaze.format()`) emits the same colors, because they were
|
|
236
|
+
compensating internally. Dropping the redundant `×100 ÷100` round-trip shifts
|
|
237
|
+
float noise by an ULP, which shows up nowhere except the (meaningless) hue term
|
|
238
|
+
of a fully desaturated `hsl()` string. `toTone` / `fromTone` still speak the
|
|
239
|
+
0–100 tone axis the authoring API takes, so divide a tone from them by 100
|
|
240
|
+
before passing it to `formatOkhst`.
|
|
241
|
+
|
|
242
|
+
A leftover `* 100` is no longer silent: a value above 1 cannot be a factor, so
|
|
243
|
+
the writers `console.warn` once per writer, and you get an obviously broken
|
|
244
|
+
string such as `okhsl(280 7040.68% 5000%)` instead of a plausible wrong color.
|
|
245
|
+
See [format writers](./reference/utilities.md#format-writers).
|
|
246
|
+
|
|
247
|
+
### 0.14 — `lightness` became `tone`
|
|
248
|
+
|
|
249
|
+
Colors used to be authored on OKHSL lightness. They are now authored on
|
|
250
|
+
**tone**, a contrast-uniform 0–100 axis (see [Tone and OKHST](./okhst.mdx)):
|
|
251
|
+
|
|
252
|
+
- The `lightness` prop is gone everywhere (theme colors, `glaze.color()`
|
|
253
|
+
structured input, tone deltas); use `tone`. Equal tone steps give equal
|
|
254
|
+
WCAG contrast, so numeric values don't map to the same OKHSL lightness as
|
|
255
|
+
before. Re-check absolute mid-range values.
|
|
256
|
+
- The config windows `lightLightness` / `darkLightness` became `lightTone` /
|
|
257
|
+
`darkTone`, and `darkCurve` was removed.
|
|
258
|
+
- `ResolvedColorVariant` stores `{ h, s, t, alpha }` instead of `{ h, s, l }`.
|
|
259
|
+
Use `variantToOkhsl()` to recover OKHSL lightness.
|
|
260
|
+
- Tone deltas that overshoot `[0, 100]` mirror to the other side of the base
|
|
261
|
+
by default instead of clamping. Set `autoFlip: false` on the color
|
|
262
|
+
(or globally) to clamp.
|
|
333
263
|
|
|
334
264
|
## Common pitfalls
|
|
335
265
|
|
|
336
|
-
| Symptom
|
|
337
|
-
|
|
|
338
|
-
|
|
|
339
|
-
|
|
|
340
|
-
| Brand text washes out against the dark surface.
|
|
341
|
-
| Tokens look right in light
|
|
342
|
-
| A
|
|
343
|
-
| `palette.tokens()` emits unexpected unprefixed names.
|
|
344
|
-
| `
|
|
345
|
-
| `
|
|
346
|
-
| `
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
- [methodology.md](methodology.md) — how to design the palette in the first place.
|
|
351
|
-
- [api.md](api.md) — full reference for every option mentioned here.
|
|
266
|
+
| Symptom | Cause | Fix |
|
|
267
|
+
| --- | --- | --- |
|
|
268
|
+
| A disabled state stops looking disabled in dark mode. | An alpha-tinted overlay on `surface-text` (which inverts) gives asymmetric perceived contrast. | Replace it with an adaptive color anchored to `surface`: a tone delta for visual spacing, or a contrast floor when one is required. See [chips and disabled states](./methodology.mdx#chips-and-disabled-states). |
|
|
269
|
+
| A brand fill turns dark in dark mode and stops reading as the brand. | The default `mode: 'auto'` inverts the tone. | Set `mode: 'fixed'` so the tone is remapped into the dark window, not inverted. See [adaptation modes](./schemes.mdx#adaptation-modes). |
|
|
270
|
+
| Brand text washes out against the dark surface. | The foreground was anchored to `accent-surface` (the brand fill), so contrast was enforced only against that fill, not against the surface it sits on. | Anchor `accent-text` and friends to `surface` with `mode: 'auto'`. |
|
|
271
|
+
| Tokens look right in light but broken in high contrast. | High contrast bypasses the tone window, so absolute tones and deltas land on the full `[0, 100]` range (a white `auto` page is black in dark high contrast), and floors without an explicit high-contrast value rise a tier (`'AA'` → `'AAA'`, APCA +15 Lc). | Add explicit `[normal, highContrast]` pairs to `tone` / `contrast` for the affected tokens. |
|
|
272
|
+
| A tone delta like `'+48'` lands on the _wrong_ (darker) side of its base. | Overshooting tone deltas mirror to the other side of the base by default (a color's `autoFlip` defaults to the global `autoFlip`, which is on). | Set `autoFlip: false` on the color to clamp to the boundary instead, or use `tone: 'max'` / `'min'` to force the extreme. |
|
|
273
|
+
| `palette.tokens()` emits unexpected unprefixed names. | A `primary` was set on the palette (or per call) and duplicates that theme's tokens without a prefix. | Pass `primary: false` to disable it for that export, or change `glaze.palette(themes, { primary })`. See [the `primary` alias](./exporting.mdx#the-primary-alias). |
|
|
274
|
+
| `glaze: token "surface" from theme "danger" collides with theme "default" — skipping.` | Two themes resolved to the same output name under your prefix config. | Adjust the prefix map so each name is unique, or accept first-write-wins. See [Collisions](./exporting.mdx#collisions). |
|
|
275
|
+
| `glaze: formatOkhsl() got a value above 1 …` | Pre-2.0 percentage-scale input to a `format*` writer (a leftover `* 100`). | Pass the 0–1 factors the converters return; see [2.0 — `format*` takes 0–1](#20--format-takes-01). |
|
|
276
|
+
| `glaze: color "X" cannot meet WCAG … in light scheme (got …)` | The contrast target is physically unreachable for the color's hue and saturation against its base. The color pins to the extreme of the side it searched first, which may not be the closer one; see [When a floor can't be met](./contrast.mdx#when-a-floor-cant-be-met). | Lower the floor or change the base. If only the high-contrast schemes fail, give `contrast` an explicit, reachable high-contrast entry (`['AA', 'AA']`). On standalone colors, set `name` so the warning is identifiable; see [`name`](./reference/color-tokens.md#name). |
|
|
277
|
+
| `glaze: shadow "X" cannot reach a 10.0-tone step …` (or `glow`) | The surface has fewer tones of room than `intensity` asks for: a glow on white, a deep shadow on near-black, or any shadow on a light `auto` page in dark high contrast (at or near tone 0 there). The overlay is pinned to its strongest composite (transparent when the surface is already at the pigment's extreme). | Lower `intensity` or give the overlay a surface with room in every scheme, and use glows for dark elevation. Otherwise expect the warning: every color resolves in all four schemes, so not using the token in a scheme doesn't silence it. See [Unreachable steps](./shadows-and-glows.mdx#unreachable-steps). |
|
|
278
|
+
| `glaze.themeFrom: this snapshot is export version 1 (Glaze 2) …` | A 2.x snapshot with shadow colors or `shadowTuning`; shadows changed meaning in 3.0. | Re-author the shadows and export again; see [3.0 — shadows and glows](#30--shadows-and-glows). |
|