@tenphi/glaze 0.0.0-snapshot.0fcd6cf → 0.0.0-snapshot.12fd135
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 +29 -28
- package/README.md +2 -2
- package/dist/index.cjs +604 -180
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +98 -34
- package/dist/index.d.mts +98 -34
- package/dist/index.mjs +602 -181
- package/dist/index.mjs.map +1 -1
- package/docs/exporting.mdx +2 -218
- package/docs/hdr.mdx +523 -0
- package/docs/methodology.mdx +2 -3
- package/docs/migration.md +53 -16
- package/docs/mixes.mdx +48 -5
- package/docs/okhst.mdx +2 -5
- package/docs/reference/color-definitions.md +13 -15
- package/docs/reference/color-tokens.md +11 -7
- package/docs/reference/configuration.md +28 -11
- package/docs/reference/contrast.md +4 -4
- package/docs/reference/index.md +7 -5
- package/docs/reference/output.md +31 -151
- package/docs/reference/palette.md +4 -4
- package/docs/reference/theme.md +10 -10
- package/docs/reference/utilities.md +40 -23
- package/docs/schemes.mdx +4 -5
- package/docs/shadows-and-glows.mdx +7 -225
- package/package.json +2 -2
package/docs/exporting.mdx
CHANGED
|
@@ -76,8 +76,8 @@ A few options cut across the shapes:
|
|
|
76
76
|
`$value` objects, not CSS strings, so they ignore `format`. Use `colorSpace`
|
|
77
77
|
instead: `'srgb'` (the default) or `'oklch'`.
|
|
78
78
|
- **HDR.** `modes: { hdr: true }` (`css({ hdr: true })` for `css()`) adds the
|
|
79
|
-
values of [HDR glows](./
|
|
80
|
-
|
|
79
|
+
values of [HDR glows](./hdr.mdx) next to the SDR ones. It is off by
|
|
80
|
+
default; see [Output is opt-in](./hdr.mdx#output-is-opt-in).
|
|
81
81
|
|
|
82
82
|
Here is one theme and the CSS custom properties `theme.css()` emits for it.
|
|
83
83
|
Switch to the Output tab to see the declarations, wrapped the way the
|
|
@@ -626,222 +626,6 @@ a standalone color's `css()`. The full rules, including which colors stay
|
|
|
626
626
|
inline, are in the
|
|
627
627
|
[Output formats](./reference/output.md#hue-channel-splitting-splithue) reference.
|
|
628
628
|
|
|
629
|
-
## HDR output
|
|
630
|
-
|
|
631
|
-
[HDR glows](./shadows-and-glows.mdx#hdr-glows) carry a second value for
|
|
632
|
-
displays that can show brighter than white. Every export leaves it out until
|
|
633
|
-
you turn on `modes.hdr`. Then each format adds it next to the SDR value it
|
|
634
|
-
already emits, for the schemes the other `modes` include. The examples below
|
|
635
|
-
use this theme:
|
|
636
|
-
|
|
637
|
-
```ts
|
|
638
|
-
const theme = glaze(280, 80, { glowTuning: { headroom: 1 } });
|
|
639
|
-
|
|
640
|
-
theme.colors({
|
|
641
|
-
surface: { tone: 100 },
|
|
642
|
-
'surface-glow': { type: 'glow', bg: 'surface', intensity: 12 },
|
|
643
|
-
near: { tone: 95, saturation: 0, mode: 'static' },
|
|
644
|
-
'near-glow': { type: 'glow', bg: 'near', intensity: 12 },
|
|
645
|
-
});
|
|
646
|
-
|
|
647
|
-
// For every export:
|
|
648
|
-
glaze.configure({ modes: { hdr: true } });
|
|
649
|
-
// Or per call:
|
|
650
|
-
theme.tokens({ modes: { hdr: true } });
|
|
651
|
-
theme.css({ hdr: true });
|
|
652
|
-
```
|
|
653
|
-
|
|
654
|
-
`surface-glow` has an HDR value in the light schemes only; on the dark
|
|
655
|
-
surface its step stays below white. `near-glow` has one in all four. HDR
|
|
656
|
-
values are always `color(srgb-linear R G B)`, whatever `format` you pass, and
|
|
657
|
-
stay literal under `splitHue`. Palettes apply prefixes, the `primary` alias,
|
|
658
|
-
and collision filtering to them like any other entry. `glaze.color()` tokens
|
|
659
|
-
are never glows, so the HDR options don't change their output.
|
|
660
|
-
|
|
661
|
-
No browser reliably paints these values past white yet
|
|
662
|
-
([Browsers and native apps](./shadows-and-glows.mdx#browsers-and-native-apps)).
|
|
663
|
-
Safari, and Chrome outside layers that hold HDR images, match
|
|
664
|
-
`dynamic-range: high` on HDR-capable displays and clamp the value, so a
|
|
665
|
-
neutral HDR glow paints opaque white there. That matches SDR for `near-glow`,
|
|
666
|
-
which is already opaque white, but `surface-glow` is transparent in SDR, so
|
|
667
|
-
its clamped value covers anything drawn between the surface and the glow.
|
|
668
|
-
Wire the CSS, Tasty, and Tailwind tiers only for glows used as fills, edge
|
|
669
|
-
highlights, or `box-shadow` colors
|
|
670
|
-
([Fills, not overlays](./shadows-and-glows.mdx#fills-not-overlays)). JSON and
|
|
671
|
-
DTCG feed native apps, which draw HDR today.
|
|
672
|
-
|
|
673
|
-
### CSS blocks
|
|
674
|
-
|
|
675
|
-
`css()` has no `modes`, so it takes `hdr` (default: the global `modes.hdr`)
|
|
676
|
-
and returns four more blocks in `hdr`. Wire each one after the SDR blocks,
|
|
677
|
-
under the same selector plus `(dynamic-range: high)`:
|
|
678
|
-
|
|
679
|
-
```ts
|
|
680
|
-
const css = theme.css({ hdr: true });
|
|
681
|
-
const hdr = css.hdr!;
|
|
682
|
-
const dark = '(prefers-color-scheme: dark)';
|
|
683
|
-
const more = '(prefers-contrast: more)';
|
|
684
|
-
const high = '(dynamic-range: high)';
|
|
685
|
-
|
|
686
|
-
const stylesheet = `
|
|
687
|
-
:root { ${css.light} }
|
|
688
|
-
@media ${dark} { :root { ${css.dark} } }
|
|
689
|
-
@media ${more} { :root { ${css.lightContrast} } }
|
|
690
|
-
@media ${dark} and ${more} { :root { ${css.darkContrast} } }
|
|
691
|
-
|
|
692
|
-
@media ${high} { :root { ${hdr.light} } }
|
|
693
|
-
@media ${dark} and ${high} { :root { ${hdr.dark} } }
|
|
694
|
-
@media ${more} and ${high} { :root { ${hdr.lightContrast} } }
|
|
695
|
-
@media ${dark} and ${more} and ${high} {
|
|
696
|
-
:root { ${hdr.darkContrast} }
|
|
697
|
-
}
|
|
698
|
-
`;
|
|
699
|
-
```
|
|
700
|
-
|
|
701
|
-
The blocks are dense. Each lists every color that has an HDR value in any
|
|
702
|
-
scheme, and repeats a color's SDR declaration where its own scheme has none:
|
|
703
|
-
|
|
704
|
-
```css
|
|
705
|
-
/* hdr.light */
|
|
706
|
-
--surface-glow-color: color(srgb-linear 1.4631 1.4631 1.4631);
|
|
707
|
-
--near-glow-color: color(srgb-linear 1.2494 1.2494 1.2494);
|
|
708
|
-
|
|
709
|
-
/* hdr.dark */
|
|
710
|
-
--surface-glow-color: oklch(1 0 0 / 0.1237);
|
|
711
|
-
--near-glow-color: color(srgb-linear 1.2494 1.2494 1.2494);
|
|
712
|
-
```
|
|
713
|
-
|
|
714
|
-
On a dark HDR display, both the light and the dark HDR blocks match. The dark
|
|
715
|
-
one comes later, so its `surface-glow` line puts the dark SDR value back. A
|
|
716
|
-
block that listed only HDR values would let the light one leak into dark.
|
|
717
|
-
When no glow passes white, all four blocks are empty strings.
|
|
718
|
-
|
|
719
|
-
### Tasty keys
|
|
720
|
-
|
|
721
|
-
`tasty()` adds a key under the `states.hdr` alias (default
|
|
722
|
-
`'@media(dynamic-range: high)'`) right after each SDR key that has an HDR
|
|
723
|
-
value:
|
|
724
|
-
|
|
725
|
-
```ts
|
|
726
|
-
theme.tasty({ modes: { hdr: true } })['#surface-glow'];
|
|
727
|
-
// → {
|
|
728
|
-
// '': 'oklch(1 0 0 / 0)',
|
|
729
|
-
// '@media(dynamic-range: high)':
|
|
730
|
-
// 'color(srgb-linear 1.4631 1.4631 1.4631)',
|
|
731
|
-
// '@media(prefers-color-scheme: dark)': 'oklch(1 0 0 / 0.1237)',
|
|
732
|
-
// }
|
|
733
|
-
```
|
|
734
|
-
|
|
735
|
-
Tasty gives later keys priority, so the order is what keeps the light HDR
|
|
736
|
-
value out of dark: `''`, hdr, dark, dark & hdr, hc, hc & hdr, dark & hc,
|
|
737
|
-
dark & hc & hdr, with only the keys that exist. Don't sort the keys if you
|
|
738
|
-
post-process the map. With your own aliases, set `states.hdr` next to `dark`
|
|
739
|
-
and `highContrast` (for example `'@hdr'`, which emits keys such as
|
|
740
|
-
`'@dark & @hdr'`) and register it in Tasty like the others.
|
|
741
|
-
|
|
742
|
-
### Tailwind blocks
|
|
743
|
-
|
|
744
|
-
`tailwind()` appends the same dense blocks after all its SDR blocks, under
|
|
745
|
-
`hdrSelector` (default `'@media (dynamic-range: high)'`), around
|
|
746
|
-
`darkSelector` and `highContrastSelector`:
|
|
747
|
-
|
|
748
|
-
```css
|
|
749
|
-
/* ...the @theme and .dark blocks, unchanged... */
|
|
750
|
-
|
|
751
|
-
@media (dynamic-range: high) {
|
|
752
|
-
:root {
|
|
753
|
-
--color-surface-glow: color(srgb-linear 1.4631 1.4631 1.4631);
|
|
754
|
-
--color-near-glow: color(srgb-linear 1.2494 1.2494 1.2494);
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
@media (dynamic-range: high) {
|
|
759
|
-
.dark {
|
|
760
|
-
--color-surface-glow: oklch(1 0 0 / 0.1237);
|
|
761
|
-
--color-near-glow: color(srgb-linear 1.2494 1.2494 1.2494);
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
```
|
|
765
|
-
|
|
766
|
-
### JSON and tokens
|
|
767
|
-
|
|
768
|
-
`json()` adds a sparse `<scheme>Hdr` key after each scheme that has an HDR
|
|
769
|
-
value. `tokens()` adds a dense `<scheme>Hdr` map after each scheme it emits,
|
|
770
|
-
always present and possibly empty:
|
|
771
|
-
|
|
772
|
-
```ts
|
|
773
|
-
theme.json({ modes: { hdr: true } })['surface-glow'];
|
|
774
|
-
// → {
|
|
775
|
-
// light: 'oklch(1 0 0 / 0)',
|
|
776
|
-
// lightHdr: 'color(srgb-linear 1.4631 1.4631 1.4631)',
|
|
777
|
-
// dark: 'oklch(1 0 0 / 0.1237)',
|
|
778
|
-
// }
|
|
779
|
-
|
|
780
|
-
theme.tokens({ modes: { hdr: true } }).darkHdr;
|
|
781
|
-
// → {
|
|
782
|
-
// 'surface-glow': 'oklch(1 0 0 / 0.1237)',
|
|
783
|
-
// 'near-glow': 'color(srgb-linear 1.2494 1.2494 1.2494)',
|
|
784
|
-
// }
|
|
785
|
-
```
|
|
786
|
-
|
|
787
|
-
A dense map drops into a cascade the way the CSS blocks do; the sparse JSON
|
|
788
|
-
keys suit code that reads one color at a time.
|
|
789
|
-
|
|
790
|
-
### DTCG and native apps
|
|
791
|
-
|
|
792
|
-
`dtcg()` and `dtcgResolver()` keep `$value` SDR, since the DTCG color format
|
|
793
|
-
has no HDR space and its components stop at 1. The HDR value goes in
|
|
794
|
-
`$extensions`, only in the scheme documents that have one. This is
|
|
795
|
-
`light['surface-glow']`; `dark['surface-glow']` has no `$extensions`:
|
|
796
|
-
|
|
797
|
-
```json
|
|
798
|
-
{
|
|
799
|
-
"$type": "color",
|
|
800
|
-
"$value": {
|
|
801
|
-
"colorSpace": "srgb",
|
|
802
|
-
"components": [1, 1, 1],
|
|
803
|
-
"hex": "#ffffff",
|
|
804
|
-
"alpha": 0
|
|
805
|
-
},
|
|
806
|
-
"$extensions": {
|
|
807
|
-
"me.tenphi.glaze": {
|
|
808
|
-
"hdr": {
|
|
809
|
-
"exposure": 0.548987,
|
|
810
|
-
"tone": 112,
|
|
811
|
-
"base": {
|
|
812
|
-
"colorSpace": "srgb",
|
|
813
|
-
"components": [1, 1, 1],
|
|
814
|
-
"hex": "#ffffff"
|
|
815
|
-
},
|
|
816
|
-
"value": {
|
|
817
|
-
"colorSpace": "srgb-linear",
|
|
818
|
-
"components": [1.463058, 1.463058, 1.463058]
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
```
|
|
825
|
-
|
|
826
|
-
- `base` is the opaque SDR color the exposure applies to: always a DTCG sRGB
|
|
827
|
-
color with `hex`, whatever `colorSpace` you export, and never pastel.
|
|
828
|
-
- `exposure` is the gain over `base` in stops.
|
|
829
|
-
- `value` is `base × 2^exposure` as linear sRGB. Only its brightest component
|
|
830
|
-
is guaranteed above 1.
|
|
831
|
-
- `tone` is the HDR value's tone: past 100 when the headroom covers the step,
|
|
832
|
-
though a saturated base that the cap stops early can land below.
|
|
833
|
-
|
|
834
|
-
Native platforms take one of the two forms:
|
|
835
|
-
|
|
836
|
-
- **UIKit and AppKit** (iOS and macOS 26):
|
|
837
|
-
`UIColor(red:green:blue:alpha:exposure:)`, or its `NSColor` twin, with
|
|
838
|
-
`base.components` and `exposure`.
|
|
839
|
-
- **SwiftUI:** `Color(.sRGB, red:green:blue:)` from `base.components`, then
|
|
840
|
-
`.exposureAdjust(exposure)`.
|
|
841
|
-
- **Android:** `Color.pack(r, g, b, 1f, space)` from `value.components`, with
|
|
842
|
-
`ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB)` as the space, in a
|
|
843
|
-
window set to `COLOR_MODE_HDR`.
|
|
844
|
-
|
|
845
629
|
## Next steps
|
|
846
630
|
|
|
847
631
|
- [Migration and upgrading](./migration.md): move an existing color system to
|