@tenphi/glaze 1.1.0 → 1.2.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/dist/index.cjs +246 -95
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +98 -6
- package/dist/index.d.mts +98 -6
- package/dist/index.mjs +246 -95
- package/dist/index.mjs.map +1 -1
- package/docs/api.md +90 -7
- package/docs/methodology.md +28 -0
- package/docs/okhst.md +21 -1
- package/package.json +1 -1
package/docs/api.md
CHANGED
|
@@ -11,6 +11,7 @@ Full reference for every public method, option, and type exported by `@tenphi/gl
|
|
|
11
11
|
- [Tailwind CSS](#themetailwindoptions)
|
|
12
12
|
- [High-contrast pairs](#high-contrast-pairs)
|
|
13
13
|
- [Color definitions](#color-definitions)
|
|
14
|
+
- [Dark seed](#dark-seed-darkhue--darksaturation)
|
|
14
15
|
- [Standalone color tokens](#standalone-color-tokens)
|
|
15
16
|
- [Shadows](#shadows)
|
|
16
17
|
- [Mix colors](#mix-colors)
|
|
@@ -31,7 +32,7 @@ Full reference for every public method, option, and type exported by `@tenphi/gl
|
|
|
31
32
|
| Method | Description |
|
|
32
33
|
| ------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
|
|
33
34
|
| `glaze(hue, saturation?, config?)` | Create a theme from hue (0–360) and saturation (0–100). Optional `config` overrides the global config for this theme. |
|
|
34
|
-
| `glaze({ hue, saturation }, config?)` | Create a theme from an options object, with optional per-theme config override.
|
|
35
|
+
| `glaze({ hue, saturation, darkHue?, darkSaturation? }, config?)` | Create a theme from an options object, with optional per-theme config override. `darkHue` / `darkSaturation` seed the dark schemes — see [Dark seed](#dark-seed-darkhue--darksaturation). |
|
|
35
36
|
| `glaze.themeFrom(data)` | Create a theme from a `theme.export()` snapshot (`kind: 'theme'`). |
|
|
36
37
|
| `glaze.from(data)` | Compat alias for `glaze.themeFrom`. |
|
|
37
38
|
| `glaze.fromHex(hex)` | Create a theme from a hex color (`#rgb` or `#rrggbb`). Extracts hue and saturation. |
|
|
@@ -45,6 +46,7 @@ Full reference for every public method, option, and type exported by `@tenphi/gl
|
|
|
45
46
|
```ts
|
|
46
47
|
const a = glaze(280, 80);
|
|
47
48
|
const b = glaze({ hue: 280, saturation: 80 });
|
|
49
|
+
const bDark = glaze({ hue: 280, saturation: 80, darkHue: 268, darkSaturation: 65 });
|
|
48
50
|
const c = glaze.fromHex('#7a4dbf');
|
|
49
51
|
const d = glaze.fromRgb(122, 77, 191);
|
|
50
52
|
const e = glaze.themeFrom(a.export());
|
|
@@ -75,6 +77,8 @@ A `GlazeTheme` exposes:
|
|
|
75
77
|
| ------------------------------- | --------------------------------------------------------------------------------------------------- |
|
|
76
78
|
| `theme.hue` (readonly) | The hue seed (0–360). |
|
|
77
79
|
| `theme.saturation` (readonly) | The saturation seed (0–100). |
|
|
80
|
+
| `theme.darkHue` (readonly) | The dark-scheme hue seed (0–360), or `undefined` when `hue` is reused. |
|
|
81
|
+
| `theme.darkSaturation` (readonly) | The dark-scheme saturation seed (0–100), or `undefined` when `saturation` is reused. |
|
|
78
82
|
| `theme.colors(defs)` | Add/replace colors (additive merge — adds new, overwrites existing by name, doesn't remove others). |
|
|
79
83
|
| `theme.color(name)` | Get a color definition by name. |
|
|
80
84
|
| `theme.color(name, def)` | Set a single color definition. |
|
|
@@ -128,6 +132,8 @@ const highSat = base.extend({ config: { darkTone: [10, 100] } });
|
|
|
128
132
|
| ------------ | --------------------- | -------------------------------------------------------------------------------------------- |
|
|
129
133
|
| `hue` | `number` | Replace the hue seed. Defaults to the parent's hue. |
|
|
130
134
|
| `saturation` | `number` | Replace the saturation seed. Defaults to the parent's saturation. |
|
|
135
|
+
| `darkHue` | `number` | Replace the dark hue seed. Defaults to the parent's `darkHue`. |
|
|
136
|
+
| `darkSaturation` | `number` | Replace the dark saturation seed. Defaults to the parent's `darkSaturation`. |
|
|
131
137
|
| `colors` | `ColorMap` | Per-theme overrides (additive merge over the inherited map). |
|
|
132
138
|
| `config` | `GlazeConfigOverride` | Config override for the child. Shallow-merged with the parent's override — child fields win. |
|
|
133
139
|
|
|
@@ -417,6 +423,8 @@ type ColorDef = RegularColorDef | ShadowColorDef | MixColorDef;
|
|
|
417
423
|
| `tone` | `HCPair<ToneValue>` | Number = absolute (0–100). `'+N'`/`'-N'` = a signed **tone delta** from the base (requires `base`). `'max'`/`'min'` = forced to the scheme's tone extreme (no `base`). Optional HC pair `[normal, hc]`. |
|
|
418
424
|
| `saturation` | `number` | Saturation factor applied to the seed saturation (0–1). Default: `1`. |
|
|
419
425
|
| `hue` | `number \| RelativeValue` | Number = absolute (0–360). String (`'+N'`/`'-N'`) = relative to the **theme seed hue** (never to a base color). |
|
|
426
|
+
| `darkHue` | `number \| RelativeValue` | Dark-scheme hue. Number = absolute (0–360). String = relative to the **theme dark seed hue**. Falls back to `hue`. See [Dark seed](#dark-seed-darkhue--darksaturation). |
|
|
427
|
+
| `darkSaturation` | `number` | Dark-scheme saturation factor (0–1) over the dark seed saturation. Falls back to `saturation`. When set, the global `darkDesaturation` reduction is **not** applied on top. |
|
|
420
428
|
| `base` | `string` | Name of another color in the same theme — makes this a _dependent_ color. |
|
|
421
429
|
| `contrast` | `HCPair<ContrastSpec>` | Contrast floor against `base`. Requires `base`. See [`contrast`](#contrast-floor). |
|
|
422
430
|
| `mode` | `'auto' \| 'fixed' \| 'static'` | Adaptation mode. Default: `'auto'`. See [Adaptation modes](#adaptation-modes). |
|
|
@@ -437,7 +445,7 @@ can drift in measured luminance. See [OKHST in Glaze](okhst.md). To port old
|
|
|
437
445
|
| ------------------- | ----------------------- | --------------------------------------------------------------------------------------------- |
|
|
438
446
|
| Number (absolute) | `tone: 45` | Absolute tone 0–100. |
|
|
439
447
|
| String (tone delta) | `tone: '-52'` | Signed difference from the base color's resolved tone (requires `base`). |
|
|
440
|
-
| Extreme | `tone: 'max'` / `'min'` | Force to the scheme's highest (`'max'` = 100) or lowest (`'min'` = 0) tone. No `base` needed. |
|
|
448
|
+
| Extreme | `tone: 'max'` / `'min'` | Force to the scheme's highest (`'max'` = 100) or lowest (`'min'` = 0) tone. With a `base`, the light-scheme shift is replayed in dark. No `base` needed. |
|
|
441
449
|
| HC pair | `tone: ['-7', '-20']` | `[normal, high-contrast]`. A single value applies to both. |
|
|
442
450
|
|
|
443
451
|
**Absolute tone** on a dependent color (`base` set) positions the color independently. In dark mode it is tone-mapped (inverted + windowed) on its own. The `contrast` solver acts as a safety net.
|
|
@@ -447,9 +455,21 @@ It gives an exact contrast step for neutrals and a stable visual progression for
|
|
|
447
455
|
chromatic colors. In dark mode with `mode: 'auto'`, it is anchored to the
|
|
448
456
|
base's per-scheme tone. If `base + delta` falls outside `[0, 100]`, the result
|
|
449
457
|
is clamped to the boundary, or — with `autoFlip` (default on) — mirrored to the
|
|
450
|
-
other side of the base.
|
|
458
|
+
other side of the base. If the mirrored target is also out of range, the original
|
|
459
|
+
delta is kept and clamped on the authored side.
|
|
451
460
|
|
|
452
|
-
**Extreme tone** (`'max'` / `'min'`) forces the color to the scheme's tone extreme without a contrast hack or a magic number. `'max'` resolves to author tone 100 and `'min'` to 0
|
|
461
|
+
**Extreme tone** (`'max'` / `'min'`) forces the color to the scheme's tone extreme without a contrast hack or a magic number. `'max'` resolves to author tone 100 and `'min'` to 0. Without a `base`, both flow through scheme mapping like an absolute tone, so under `mode: 'auto'` they invert in dark (`'max'` is lightest in light, darkest in dark). Use `mode: 'static'` to pin the same extreme across schemes, or `mode: 'fixed'` to keep the same end without inverting. No `base` required.
|
|
462
|
+
|
|
463
|
+
**Extreme tone with a `base`** keeps the pair's contrast across schemes. The tone windows are asymmetric by default (`lightTone: [10, 100]`, `darkTone: [15, 95]`), so re-mapping the extreme through the dark window would squeeze the base-to-extreme span and cost contrast. Instead Glaze measures the tone shift the light scheme applied between the base and the extreme, then replays it against the base's resolved dark tone — mirrored under `mode: 'auto'` (both ends invert), same-signed under `'fixed'`. The result is clamped to `[0, 100]` only, so it may cross the `darkTone` window edge; that is intentional, since the author asked for the extreme. When the shift does not fit above or below the dark base, the color pins at tone 100 / 0. `mode: 'static'` is unaffected, and high-contrast variants already use the full range.
|
|
464
|
+
|
|
465
|
+
```ts
|
|
466
|
+
theme.colors({
|
|
467
|
+
bg: { tone: 60 },
|
|
468
|
+
fg: { base: 'bg', tone: 'max' },
|
|
469
|
+
});
|
|
470
|
+
// light: bg 62.0, fg 100.0 — shift +38.0, contrast 3.18:1
|
|
471
|
+
// dark: bg 43.9, fg 5.9 — shift -38.0, contrast 3.18:1
|
|
472
|
+
```
|
|
453
473
|
|
|
454
474
|
A dependent color with `base` but no `tone` inherits the base's tone (equivalent to a delta of 0).
|
|
455
475
|
|
|
@@ -457,7 +477,7 @@ A dependent color with `base` but no `tone` inherits the base's tone (equivalent
|
|
|
457
477
|
|
|
458
478
|
`autoFlip` governs what happens when a result would fall outside its valid range:
|
|
459
479
|
|
|
460
|
-
- **Relative `tone` overshoot:** when `base ± delta` exceeds `[0, 100]`, `autoFlip` mirrors the delta to the other side of the base (e.g. `'+30'` becomes `'-30'`) instead of clamping to the boundary.
|
|
480
|
+
- **Relative `tone` overshoot:** when `base ± delta` exceeds `[0, 100]`, `autoFlip` mirrors the delta to the other side of the base (e.g. `'+30'` becomes `'-30'`) instead of clamping to the boundary. If the mirrored target is also outside `[0, 100]`, the original delta is kept and clamped on the authored side.
|
|
461
481
|
- **`contrast` direction:** when the requested tone direction can't meet the floor, `autoFlip` lets the solver try the opposite side (the same behavior as the global `autoFlip`).
|
|
462
482
|
|
|
463
483
|
`autoFlip` defaults to the global `autoFlip` (`true`). Set `autoFlip: false` on a color to clamp instead of mirror — useful when you want a relative offset to stay on the authored side of the base, or to keep an unmet contrast pinned to one direction's extreme.
|
|
@@ -548,6 +568,48 @@ theme.colors({
|
|
|
548
568
|
|
|
549
569
|
Relative hue is always relative to the **theme seed hue**, not to a base color.
|
|
550
570
|
|
|
571
|
+
#### Dark seed (`darkHue` / `darkSaturation`)
|
|
572
|
+
|
|
573
|
+
Tone inverts automatically between light and dark, but hue and saturation do
|
|
574
|
+
not — by default every scheme reuses the one seed, with dark getting a flat
|
|
575
|
+
`darkDesaturation` haircut. When a palette needs a genuinely different chroma in
|
|
576
|
+
dark (a cooler accent, a calmer surface tint), author a second seed:
|
|
577
|
+
|
|
578
|
+
```ts
|
|
579
|
+
const theme = glaze({
|
|
580
|
+
hue: 280,
|
|
581
|
+
saturation: 80,
|
|
582
|
+
darkHue: 268, // dark schemes seed from 268 instead of 280
|
|
583
|
+
darkSaturation: 65, // and from 65 instead of 80
|
|
584
|
+
});
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
Individual colors can override either channel for dark on their own:
|
|
588
|
+
|
|
589
|
+
```ts
|
|
590
|
+
theme.colors({
|
|
591
|
+
surface: { tone: 97 },
|
|
592
|
+
accent: { tone: 55, hue: '+20', darkHue: '+35' },
|
|
593
|
+
warning: { tone: 60, saturation: 0.9, darkSaturation: 0.6 },
|
|
594
|
+
});
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
Both apply to the `dark` **and** `darkContrast` variants. The rules:
|
|
598
|
+
|
|
599
|
+
| Rule | Behavior |
|
|
600
|
+
| ---- | -------- |
|
|
601
|
+
| Units | Seed-level `darkSaturation` is `0–100` (like `saturation` on the theme). Def-level `darkSaturation` is a `0–1` factor (like `saturation` on a color def). |
|
|
602
|
+
| Fallback | `darkHue` falls back to `hue`, `darkSaturation` to `saturation`, and the def falls back to the seed. Omitting everything reproduces the previous behavior exactly. |
|
|
603
|
+
| Relative hue | `darkHue: '+N'` anchors to the theme's **dark** seed hue. A plain `hue: '+N'` with no `darkHue` also re-anchors to the dark seed, so a whole palette rotates together. |
|
|
604
|
+
| Absolute hue | An absolute `hue` (no `darkHue`) is used verbatim in dark — absolute means absolute in every scheme. |
|
|
605
|
+
| `darkDesaturation` | Bypassed as soon as any explicit dark saturation is authored, at either level. The value you write is the value you get. |
|
|
606
|
+
| `mode: 'static'` | Ignores both. A static color is pinned to one hue and saturation across every scheme. |
|
|
607
|
+
| Shadows and mixes | Have no channels of their own — they derive hue and saturation from their `bg` / `fg` / `target`, so they inherit dark overrides automatically. |
|
|
608
|
+
|
|
609
|
+
With [`splitHue`](#hue-channel-splitting-splithue), a dark seed makes the hue
|
|
610
|
+
custom properties scheme-dependent; Glaze re-declares them in the dark block
|
|
611
|
+
automatically.
|
|
612
|
+
|
|
551
613
|
#### Per-color `pastel`
|
|
552
614
|
|
|
553
615
|
`pastel: true` on a single color def overrides the per-theme / per-token `pastel` override for that color only. It toggles the hue-independent "safe" chroma limit used in every OKHSL↔sRGB conversion that touches this color: luminance calculations during contrast solving, gamut clamping during sRGB blend / mix edges, and output formatting. The effective flag is carried on the resolved variant (`ResolvedColorVariant.pastel`) so formatting matches the gamut mapping applied during resolution.
|
|
@@ -687,6 +749,9 @@ glaze.color(color: GlazeFromInput | GlazeColorInput | GlazeColorValue, config?:
|
|
|
687
749
|
| `saturation` | `number` | 0–100. |
|
|
688
750
|
| `tone` | `HCPair<number \| ExtremeValue>` | 0–100 (contrast-shaped) or `'max'`/`'min'`, optional HC pair. |
|
|
689
751
|
| `saturationFactor` | `number` | Multiplier on the seed (0–1). Default: `1`. |
|
|
752
|
+
| `darkHue` | `number \| RelativeValue` | Dark-scheme hue. Absolute (0–360) or `'+N'`/`'-N'` relative to the dark seed hue (which defaults to `hue`). Falls back to `hue`. |
|
|
753
|
+
| `darkSaturation` | `number` | Dark-scheme seed saturation (0–100). Falls back to `saturation`. |
|
|
754
|
+
| `darkSaturationFactor` | `number` | Dark-scheme multiplier on the dark seed (0–1). Falls back to `saturationFactor`. |
|
|
690
755
|
| `mode` | `AdaptationMode` | Default: `'auto'`. |
|
|
691
756
|
| `autoFlip` | `boolean` | Flip out-of-bounds results instead of clamping. Default: global `autoFlip`. |
|
|
692
757
|
| `opacity` | `number` | Fixed alpha 0–1. |
|
|
@@ -705,6 +770,9 @@ glaze.color(color: GlazeFromInput | GlazeColorInput | GlazeColorValue, config?:
|
|
|
705
770
|
| `saturation` | Override seed saturation (0–100). |
|
|
706
771
|
| `tone` | Number (absolute 0–100), `'+N'`/`'-N'`, or `'max'`/`'min'`. Without `base`, relative anchors to the seed; with `base`, anchors to `base`'s tone per scheme. |
|
|
707
772
|
| `saturationFactor` | Multiplier on the seed (0–1). |
|
|
773
|
+
| `darkHue` | Dark-scheme hue: absolute (0–360) or `'+N'`/`'-N'` relative to the dark seed hue. Falls back to `hue`. |
|
|
774
|
+
| `darkSaturation` | Dark-scheme seed saturation (0–100). Falls back to `saturation`. |
|
|
775
|
+
| `darkSaturationFactor` | Dark-scheme multiplier on the dark seed (0–1). Falls back to `saturationFactor`. |
|
|
708
776
|
| `mode` | `'auto'` (default) / `'fixed'` / `'static'`. |
|
|
709
777
|
| `autoFlip` | Flip out-of-bounds results instead of clamping. Default: global `autoFlip`. |
|
|
710
778
|
| `contrast` | Contrast floor (WCAG or APCA). Without `base`, anchored to the literal seed; with `base`, solved per scheme. |
|
|
@@ -1357,6 +1425,21 @@ On `theme.css()`, `theme.tasty()`, `palette.css()`, `palette.tasty()`, and stand
|
|
|
1357
1425
|
|
|
1358
1426
|
**Requirements:** every exported color must be pastel (`pastel: true` on the theme/token override or per-color). Pastel mode bounds chroma by the hue-independent safe chroma at each lightness, so emitted `C` stays in sRGB for any rotated hue. Non-pastel palettes throw rather than emit values that would clip under rotation.
|
|
1359
1427
|
|
|
1428
|
+
**Dark hues:** hue vars are scheme-independent unless the theme or a color authors a [dark hue](#dark-seed-darkhue--darksaturation). When one does, the whole hue set is re-declared in the dark block (and in the Tasty dark state, gated by `modes.dark`):
|
|
1429
|
+
|
|
1430
|
+
```css
|
|
1431
|
+
/* glaze({ hue: 240, saturation: 18, darkHue: 200 }) */
|
|
1432
|
+
:root {
|
|
1433
|
+
--brand-hue: 240;
|
|
1434
|
+
--accent-hue: calc(var(--brand-hue) + 20);
|
|
1435
|
+
}
|
|
1436
|
+
/* dark */
|
|
1437
|
+
--brand-hue: 200;
|
|
1438
|
+
--accent-hue: calc(var(--brand-hue) + 20);
|
|
1439
|
+
```
|
|
1440
|
+
|
|
1441
|
+
`--accent-hue` repeats verbatim on purpose: a custom property substitutes `var()` at computed-value time on the element that declares it, so it has to be re-declared to pick up the new `--brand-hue`. Colors with `mode: 'static'` are pinned to an absolute hue instead, so they don't drift with the dark seed.
|
|
1442
|
+
|
|
1360
1443
|
**Limitations:** `oklch` only (native CSS `var()` in the hue slot). Shadow and mix colors stay inline (blended hue). Standalone `.token()` / `.tasty()` do not support `splitHue` (return shape cannot carry the `$name-hue` declaration).
|
|
1361
1444
|
|
|
1362
1445
|
`theme.dtcg()` / `theme.dtcgResolver()` / `palette.dtcg()` / `palette.dtcgResolver()` ignore `format` — DTCG emits structured `$value` objects, not CSS strings. Use the `colorSpace` option (`'srgb'` or `'oklch'`) to pick the color representation instead.
|
|
@@ -1390,7 +1473,7 @@ Light: accent-fill tone=52, accent-text tone='+20' → lighter than the fill
|
|
|
1390
1473
|
Dark: accent-fill maps into the dark window, sign preserved
|
|
1391
1474
|
```
|
|
1392
1475
|
|
|
1393
|
-
Offsets that would push past `[0, 100]` clamp to the boundary, or — with `autoFlip` (default on) — mirror to the other side of the base. Set `autoFlip: false` to keep the authored side and clamp instead.
|
|
1476
|
+
Offsets that would push past `[0, 100]` clamp to the boundary, or — with `autoFlip` (default on) — mirror to the other side of the base. If the mirror also overshoots, the original side is kept and clamped. Set `autoFlip: false` to keep the authored side and clamp instead.
|
|
1394
1477
|
|
|
1395
1478
|
**`static`** — no adaptation, same tone in every scheme.
|
|
1396
1479
|
|
|
@@ -1541,7 +1624,7 @@ available result.
|
|
|
1541
1624
|
| Relative `tone` without `base` in a **theme** color | Validation error |
|
|
1542
1625
|
| `contrast` without `base` in `glaze.color()` | Anchors against the literal seed (no error) |
|
|
1543
1626
|
| Relative `tone` without `base` in `glaze.color()` | Anchors against the literal seed (no error) |
|
|
1544
|
-
| Relative `tone` overshoots `[0, 100]` | Mirror to the other side of the base (`autoFlip` on, default), or clamp to the boundary (`autoFlip` off) |
|
|
1627
|
+
| Relative `tone` overshoots `[0, 100]` | Mirror to the other side of the base (`autoFlip` on, default), or clamp to the boundary (`autoFlip` off). If the mirror also overshoots, clamp on the authored side. |
|
|
1545
1628
|
| `tone` resolves outside 0–100 | Clamp silently |
|
|
1546
1629
|
| `'max'` / `'min'` without `base` | Allowed — resolves to the scheme's tone extreme (root color) |
|
|
1547
1630
|
| `saturation` outside 0–1 | Clamp silently |
|
package/docs/methodology.md
CHANGED
|
@@ -123,6 +123,29 @@ Per-color `saturation` is a factor of the theme seed, not an absolute
|
|
|
123
123
|
saturation. With `SEED_SATURATION = 80`, `saturation: 0.25` means one quarter of
|
|
124
124
|
that seed.
|
|
125
125
|
|
|
126
|
+
### When dark needs its own chroma
|
|
127
|
+
|
|
128
|
+
Dark schemes get a flat `darkDesaturation` haircut (10% by default), which is
|
|
129
|
+
enough for most palettes. Reach for a second seed only when dark is wrong in a
|
|
130
|
+
way a uniform reduction cannot fix — a hue that reads muddy against dark
|
|
131
|
+
surfaces, or an accent that stays gaudy no matter how much you dial the global
|
|
132
|
+
knob:
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
const primary = glaze({
|
|
136
|
+
hue: PURPLE_HUE,
|
|
137
|
+
saturation: SEED_SATURATION,
|
|
138
|
+
darkHue: PURPLE_HUE - 12, // cooler purple on dark surfaces
|
|
139
|
+
darkSaturation: 62, // calmer than a 10% haircut would give
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
An explicit `darkSaturation` replaces `darkDesaturation` rather than stacking
|
|
144
|
+
with it, so the number you write is the number you get. Prefer tuning the seed
|
|
145
|
+
over per-color `darkHue` / `darkSaturation`: one seed keeps the palette
|
|
146
|
+
coherent, while scattered per-color dark overrides drift apart as the palette
|
|
147
|
+
grows. Save the per-color form for the one or two tokens that genuinely need it.
|
|
148
|
+
|
|
126
149
|
## Naming
|
|
127
150
|
|
|
128
151
|
Prefer purpose first and variant last:
|
|
@@ -288,6 +311,11 @@ When a token needs the scheme extreme, use `tone: 'min'` or `tone: 'max'`
|
|
|
288
311
|
directly. Avoid large magic numbers or fake contrast floors just to push a color
|
|
289
312
|
to the edge.
|
|
290
313
|
|
|
314
|
+
Pair the extreme with a `base` when the gap between the two has to survive the
|
|
315
|
+
scheme switch. Glaze then replays the light-scheme tone shift against the
|
|
316
|
+
resolved dark base instead of re-mapping the extreme through the dark window,
|
|
317
|
+
so a knockout on a surface keeps the same contrast in both schemes.
|
|
318
|
+
|
|
291
319
|
## Fixed surfaces and accent fills
|
|
292
320
|
|
|
293
321
|
Use `mode: 'fixed'` when the authored color should stay recognizable across
|
package/docs/okhst.md
CHANGED
|
@@ -44,7 +44,9 @@ surface: {
|
|
|
44
44
|
}
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
`'max'` and `'min'` select the corresponding end of the active scheme.
|
|
47
|
+
`'max'` and `'min'` select the corresponding end of the active scheme. With a
|
|
48
|
+
`base`, they instead preserve their distance from that base across schemes —
|
|
49
|
+
see [Scheme adaptation](#scheme-adaptation).
|
|
48
50
|
|
|
49
51
|
### Tone delta
|
|
50
52
|
|
|
@@ -115,6 +117,24 @@ authored tone
|
|
|
115
117
|
|
|
116
118
|
Dark schemes also apply `darkDesaturation` unless the color is `static`.
|
|
117
119
|
|
|
120
|
+
**Hue and saturation are authoring inputs, not part of the tone transfer.** The
|
|
121
|
+
tone axis is the only channel OKHST adapts across schemes; `h` and `s` pass
|
|
122
|
+
through verbatim, with `darkDesaturation` as the one automatic adjustment.
|
|
123
|
+
A theme (or a single color) can seed a different hue and saturation for the
|
|
124
|
+
dark schemes via `darkHue` / `darkSaturation` — see
|
|
125
|
+
[api.md](api.md#dark-seed-darkhue--darksaturation). Those pick the `h` and `s`
|
|
126
|
+
that enter the pipeline above; they never change how tone is transferred.
|
|
127
|
+
Authoring a dark saturation replaces the `darkDesaturation` step rather than
|
|
128
|
+
stacking with it, and `static` colors ignore both.
|
|
129
|
+
|
|
130
|
+
**Extremes with a base** take a different dark path. Because the light and dark
|
|
131
|
+
windows are asymmetric, running an extreme through the dark window compresses
|
|
132
|
+
the span between it and its base, which lowers contrast. For a color with both
|
|
133
|
+
`base` and `tone: 'max'` / `'min'`, Glaze measures the tone shift the light
|
|
134
|
+
scheme produced between the two, then replays it against the base's resolved
|
|
135
|
+
dark tone (mirrored under `auto`, same-signed under `fixed`). Only `[0, 100]`
|
|
136
|
+
clamping applies, so such a color may render past the `darkTone` boundary.
|
|
137
|
+
|
|
118
138
|
## Reference and render epsilon
|
|
119
139
|
|
|
120
140
|
`REF_EPS = 0.05` defines Glaze's canonical tone scale. It is used for OKHST
|
package/package.json
CHANGED