@tenphi/glaze 1.3.0 → 1.4.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 +382 -290
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +31 -1
- package/dist/index.d.mts +31 -1
- package/dist/index.mjs +382 -290
- package/dist/index.mjs.map +1 -1
- package/docs/api.md +59 -3
- package/package.json +1 -1
package/docs/api.md
CHANGED
|
@@ -559,8 +559,9 @@ type ColorDef = RegularColorDef | ShadowColorDef | MixColorDef;
|
|
|
559
559
|
|
|
560
560
|
| Field | Type | Description |
|
|
561
561
|
| ------------ | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
562
|
-
| `
|
|
563
|
-
| `
|
|
562
|
+
| `from` | `GlazeColorValue` | Seed this color from a literal value (hex, `rgb()`, `hsl()`, `okhsl()`, `okhst()`, `oklch()`, or a value object). Supplies `hue`, `tone`, and an **absolute** saturation — the one way a theme color escapes the seed ceiling. Light/normal-contrast reproduces it exactly. See [`from`](#from-a-literal-color). |
|
|
563
|
+
| `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]`. Defaults to the tone of `from`. |
|
|
564
|
+
| `saturation` | `number` | Saturation factor applied to the seed saturation (0–1). Default: `1`. With `from`, setting it overrides the color's absolute saturation and reverts to a factor of the seed. |
|
|
564
565
|
| `hue` | `number \| RelativeValue` | Number = absolute (0–360). String (`'+N'`/`'-N'`) = relative to the **theme seed hue** (never to a base color). |
|
|
565
566
|
| `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). |
|
|
566
567
|
| `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. |
|
|
@@ -573,6 +574,61 @@ type ColorDef = RegularColorDef | ShadowColorDef | MixColorDef;
|
|
|
573
574
|
| `role` | `RoleInput` | Semantic role against `base` (`'text'` / `'surface'` / `'border'` or an alias). Fixes APCA contrast polarity. Resolved via: explicit `role` → name inference → opposite of the base's role → `'text'`. See [Roles](#roles). |
|
|
574
575
|
| `inherit` | `boolean` | Whether this color is inherited by child themes via `extend()`. Default: `true`. Set to `false` to make the color local to the current theme. |
|
|
575
576
|
|
|
577
|
+
#### `from` (a literal color)
|
|
578
|
+
|
|
579
|
+
Most of Glaze answers the question "design me a palette". `from` answers the
|
|
580
|
+
other one: **"honor this color."** White-label products, multi-tenant branding
|
|
581
|
+
and imported design tokens all arrive with a value already chosen, and it is a
|
|
582
|
+
contract rather than a starting point.
|
|
583
|
+
|
|
584
|
+
```ts
|
|
585
|
+
const theme = glaze(280, 80);
|
|
586
|
+
|
|
587
|
+
theme.colors({
|
|
588
|
+
surface: { tone: 100, saturation: 0.12 },
|
|
589
|
+
brand: { from: '#2f5bff', base: 'surface', contrast: 3 },
|
|
590
|
+
});
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
It accepts the same values as [`glaze.color()`](#input-forms) and supplies three
|
|
594
|
+
things at once: `hue`, `tone`, and — uniquely among theme colors — an
|
|
595
|
+
**absolute saturation**.
|
|
596
|
+
|
|
597
|
+
That last one is the point. Every other color's `saturation` is a 0–1 factor of
|
|
598
|
+
the theme seed, so the seed is a ceiling: without `from`, the only way to place
|
|
599
|
+
a color more saturated than its theme is to re-seed the whole theme, dragging
|
|
600
|
+
every sibling along. A `from` color carries its own chroma and is unaffected by
|
|
601
|
+
the seed:
|
|
602
|
+
|
|
603
|
+
```ts
|
|
604
|
+
// Identical output at every seed saturation — the color is the color.
|
|
605
|
+
for (const seed of [5, 50, 100]) {
|
|
606
|
+
glaze(280, seed).colors({ brand: { from: '#2f5bff' } }); // → #2f5bff
|
|
607
|
+
}
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
**What "exactly" covers.** The **light, normal-contrast** variant reproduces the
|
|
611
|
+
value (a local `lightTone: false`, the same default the value-shorthand form of
|
|
612
|
+
`glaze.color()` applies). Dark and high contrast adapt as usual. That asymmetry
|
|
613
|
+
is deliberate: those are the variants a reader reaches for when the normal one
|
|
614
|
+
does not work for them, so readability outranks fidelity there — and a color
|
|
615
|
+
pinned across all four would just be a worse `mode: 'static'`.
|
|
616
|
+
|
|
617
|
+
A `contrast` floor still applies in every scheme and is still a floor, not a
|
|
618
|
+
target: a value that already clears it is emitted untouched, and one that misses
|
|
619
|
+
moves only as far as the floor. Because the floor is solved per scheme, which
|
|
620
|
+
scheme comes out exact depends on the color — a light brand cannot clear 3:1 on
|
|
621
|
+
a white page but clears it easily on a dark one.
|
|
622
|
+
|
|
623
|
+
Sibling fields win over what the value supplied, so `{ from: '#2f5bff', hue: 300 }`
|
|
624
|
+
keeps the saturation and tone and rotates the hue. Note that a `saturation`
|
|
625
|
+
written alongside `from` reverts to its usual meaning — a factor of the seed,
|
|
626
|
+
not of the color.
|
|
627
|
+
|
|
628
|
+
A `from` color needs no `base` and no `tone`: it is placed absolutely, so it
|
|
629
|
+
stands as a root on its own. Add `base` + `contrast` when it has to stay legible
|
|
630
|
+
against something.
|
|
631
|
+
|
|
576
632
|
#### Tone values
|
|
577
633
|
|
|
578
634
|
`tone` (0–100) replaces authored OKHSL lightness with a contrast-shaped axis.
|
|
@@ -599,7 +655,7 @@ delta is kept and clamped on the authored side.
|
|
|
599
655
|
|
|
600
656
|
**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.
|
|
601
657
|
|
|
602
|
-
**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
|
|
658
|
+
**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. High-contrast variants follow the same rule, but since their window is already the full range the replay reproduces the plain mapping unless the base itself sits asymmetrically across schemes.
|
|
603
659
|
|
|
604
660
|
```ts
|
|
605
661
|
theme.colors({
|
package/package.json
CHANGED