@weasel-js/theme 1.9.4 → 1.10.1
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 +92 -5
- package/dist/chunk-32JKTYS2.js +160 -0
- package/dist/chunk-32JKTYS2.js.map +1 -0
- package/dist/chunk-354G4TUL.js +246 -0
- package/dist/chunk-354G4TUL.js.map +1 -0
- package/dist/chunk-5PFIA5JU.js +339 -0
- package/dist/chunk-5PFIA5JU.js.map +1 -0
- package/dist/{chunk-EFGZ56CE.js → chunk-JX2OC444.js} +169 -159
- package/dist/chunk-JX2OC444.js.map +1 -0
- package/dist/chunk-M3R5MV7Q.js +10 -0
- package/dist/chunk-M3R5MV7Q.js.map +1 -0
- package/dist/chunk-P5F3GZV6.js +54 -0
- package/dist/chunk-P5F3GZV6.js.map +1 -0
- package/dist/{chunk-7LCRXZDY.js → chunk-S7MJPFDP.js} +6 -208
- package/dist/chunk-S7MJPFDP.js.map +1 -0
- package/dist/{definition-GS7_Kz9D.d.ts → definition-CSeERbSL.d.ts} +79 -21
- package/dist/engine.d.ts +11 -15
- package/dist/engine.js +154 -2
- package/dist/engine.js.map +1 -1
- package/dist/index.d.ts +61 -4
- package/dist/index.js +12 -230
- package/dist/index.js.map +1 -1
- package/dist/prefs.d.ts +62 -0
- package/dist/prefs.js +193 -0
- package/dist/prefs.js.map +1 -0
- package/dist/react.d.ts +15 -4
- package/dist/react.js +18 -7
- package/dist/react.js.map +1 -1
- package/dist/themes/platinum.d.ts +22 -0
- package/dist/themes/platinum.js +802 -0
- package/dist/themes/platinum.js.map +1 -0
- package/dist/themes/win9x.d.ts +16 -0
- package/dist/themes/win9x.js +788 -0
- package/dist/themes/win9x.js.map +1 -0
- package/dist/tokens.css +24 -1
- package/package.json +16 -3
- package/dist/chunk-7LCRXZDY.js.map +0 -1
- package/dist/chunk-EFGZ56CE.js.map +0 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ definition into CSS custom properties plus a parallel TS export.
|
|
|
5
5
|
|
|
6
6
|
Part of [weasel](https://github.com/orochi235/weasel), a domain-agnostic 2D
|
|
7
7
|
scene-graph canvas kit for React. See the
|
|
8
|
-
[API reference](https://orochi235.github.io/weasel/
|
|
8
|
+
[API reference](https://orochi235.github.io/weasel/#pkg/theme).
|
|
9
9
|
|
|
10
10
|
## Install
|
|
11
11
|
|
|
@@ -76,6 +76,85 @@ beside it. The resolved record carries that CSS as is, because a canvas has no
|
|
|
76
76
|
`currentColor`; flatten it with `resolveCurrentColor(value, resolved['--wzl-fg'])`,
|
|
77
77
|
passing the color of the text it is drawn beside.
|
|
78
78
|
|
|
79
|
+
## Between two modes
|
|
80
|
+
|
|
81
|
+
`blendTheme(theme, weights, selection?)` is the theme partway between values
|
|
82
|
+
of its axes, as `resolveTheme` returns one:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
blendTheme(acme, { mode: { dark: 0.25, light: 0.75 } });
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
It resolves the theme at each weighted selection and mixes what each token
|
|
89
|
+
resolved to: a color in OKLCH, a number with one unit linearly, and anything
|
|
90
|
+
else the heaviest selection's. `applyTheme(el, theme, selection, { weights })`
|
|
91
|
+
holds that blend where it would hold the selection, so calling it with the
|
|
92
|
+
next weights is one frame of a crossfade.
|
|
93
|
+
|
|
94
|
+
## As a prefs schema
|
|
95
|
+
|
|
96
|
+
`@weasel-js/theme/prefs` turns a theme into a `@weasel-js/prefs` schema, and a
|
|
97
|
+
store over that schema back into a theme:
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
import { openPrefsSync } from '@weasel-js/prefs';
|
|
101
|
+
import { applyTheme, weaselTheme } from '@weasel-js/theme';
|
|
102
|
+
import { themeFromPrefs, themePrefsSchema, themePrefsSelection } from '@weasel-js/theme/prefs';
|
|
103
|
+
|
|
104
|
+
const store = openPrefsSync(themePrefsSchema(weaselTheme), { storage, prefix: 'theme.' });
|
|
105
|
+
store.set('accent.accent-base', '#ff3366');
|
|
106
|
+
store.set('surface.surface', '#fffdf8', { at: { 'axis.mode': 'light' } });
|
|
107
|
+
|
|
108
|
+
applyTheme(document.documentElement, themeFromPrefs(store, weaselTheme), themePrefsSelection(store));
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The schema holds an `enum` per axis under `axis` (`axis.mode`,
|
|
112
|
+
`axis.density`) and a page per token group, each token at `<group>.<token>`
|
|
113
|
+
with no `--wzl-`: a `color`, a `number` in the token's unit, or a `string` of
|
|
114
|
+
CSS text for a value neither holds, such as a font stack or a `var()`. Override
|
|
115
|
+
hooks are leaves too. Every leaf may be split by every axis, and its default
|
|
116
|
+
is what the theme resolves to, already split along the axes the token differs
|
|
117
|
+
by, so a store nothing was written to reads the theme as it is.
|
|
118
|
+
|
|
119
|
+
`themePrefsPins(store)` is the `pins` those values amount to. Only a leaf with
|
|
120
|
+
a value of its own is a pin, so a token left alone keeps its rule: pin
|
|
121
|
+
`accent-base` and `--wzl-accent` still follows it. A store reads such an alias
|
|
122
|
+
from the base theme, where it has not moved; `themePrefsValues(store, base)`
|
|
123
|
+
gives the values and loci for a form with each unset leaf read from the pinned
|
|
124
|
+
theme.
|
|
125
|
+
|
|
126
|
+
## Desktop themes
|
|
127
|
+
|
|
128
|
+
Each is a pins-only theme over the built-in one, on its own entry so an app
|
|
129
|
+
that never asks for it never loads it:
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
import { applyTheme } from '@weasel-js/theme';
|
|
133
|
+
import { win9xTheme } from '@weasel-js/theme/themes/win9x';
|
|
134
|
+
|
|
135
|
+
applyTheme(document.documentElement, win9xTheme); // opens in light mode
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
- `win9xTheme`: Windows 95 and 98. Gray faces in two-pixel bevels, square
|
|
139
|
+
corners, a navy-to-blue title strip, and the system sans at 11, 12, or 14px
|
|
140
|
+
by density.
|
|
141
|
+
- `platinumTheme`, from `@weasel-js/theme/themes/platinum`: Mac OS 8 and 9's
|
|
142
|
+
Platinum. Light gray faces with a black outline and a soft two-tone edge,
|
|
143
|
+
rounded buttons, white fields, and pinstriped window titles centered on the
|
|
144
|
+
strip. The same entry exports `classicMacTheme`, which it extends: the faces,
|
|
145
|
+
sizes, and geometry every classic Mac OS look shares, with no color of its
|
|
146
|
+
own. A System 7 theme is `defineTheme({ name, extends: classicMacTheme, pins })`
|
|
147
|
+
with that era's colors and edges.
|
|
148
|
+
|
|
149
|
+
They open in light mode, the only one the originals had; the dark mode is the
|
|
150
|
+
theme's own. The system faces are named first in the font stacks and are not
|
|
151
|
+
bundled, so a machine without them draws the next face in the stack. The
|
|
152
|
+
bevels and flat button faces are override hooks the theme pins (see below),
|
|
153
|
+
which the controls in `@weasel-js/ui` read wherever they draw a raised face, a
|
|
154
|
+
well, or a button that stays down; the description of each hook lists them.
|
|
155
|
+
To title every group as a window, wrap the app in
|
|
156
|
+
`<DefaultMotif motif={motifs.titlebar()}>` from `@weasel-js/ui`.
|
|
157
|
+
|
|
79
158
|
## The engine
|
|
80
159
|
|
|
81
160
|
`@weasel-js/theme/engine` is for authoring layered theme definitions: seeds,
|
|
@@ -157,10 +236,11 @@ names the token bare (`"ref": "gray-800"`).
|
|
|
157
236
|
|
|
158
237
|
## Override hooks
|
|
159
238
|
|
|
160
|
-
|
|
161
|
-
`--wzl-swatch-size`, `--wzl-timeline-label-w`,
|
|
162
|
-
the rest. Kit CSS reads each one with a
|
|
163
|
-
container is what changes it, and leaving it
|
|
239
|
+
Some custom properties restyle kit components and **the built-in theme does
|
|
240
|
+
not declare them**: `--wzl-swatch-size`, `--wzl-timeline-label-w`,
|
|
241
|
+
`--wzl-number-field-width` and the rest. Kit CSS reads each one with a
|
|
242
|
+
fallback, so setting it on any container is what changes it, and leaving it
|
|
243
|
+
alone takes the fallback:
|
|
164
244
|
|
|
165
245
|
```css
|
|
166
246
|
.dense-panel { --wzl-swatch-size: 20px; --wzl-number-field-width: 6ch; }
|
|
@@ -175,6 +255,13 @@ import { TOKEN_MANIFEST } from '@weasel-js/theme';
|
|
|
175
255
|
const hooks = TOKEN_MANIFEST.filter((t) => t.hook);
|
|
176
256
|
```
|
|
177
257
|
|
|
258
|
+
A theme built with `defineTheme` can pin one too, which is how a theme changes
|
|
259
|
+
what a token cannot say: `--wzl-bevel-raised`, `--wzl-bevel-sunken` and
|
|
260
|
+
`--wzl-bevel-pressed` are the box shadows of a raised face, a well and a pressed
|
|
261
|
+
button, and the `--wzl-button-*` hooks replace a Button's glass with a flat
|
|
262
|
+
face. A pin's value is fixed where the theme is applied, so one written with
|
|
263
|
+
`var()` does not follow a token a container overrides further down.
|
|
264
|
+
|
|
178
265
|
They stay out of `tokens.css` on purpose. A `:root` value would outrank the
|
|
179
266
|
fallback written beside every read, so the two would have to agree forever, and
|
|
180
267
|
`npm run check:token-reads` would stop requiring that the fallback be there at
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { pick, mergeAxes, fullSelection } from './chunk-P5F3GZV6.js';
|
|
2
|
+
import { parseHexBytes } from '@weasel-js/paint';
|
|
3
|
+
|
|
4
|
+
// src/dtcg/value.ts
|
|
5
|
+
function fontStack(value) {
|
|
6
|
+
return value.map((f) => typeof f === "string" && /\s/.test(f) ? `'${f}'` : String(f)).join(", ");
|
|
7
|
+
}
|
|
8
|
+
function serializeTokenValue(type, value) {
|
|
9
|
+
if (Array.isArray(value)) {
|
|
10
|
+
if (type === "cubicBezier") return `cubic-bezier(${value.join(", ")})`;
|
|
11
|
+
if (type === "fontFamily") return fontStack(value);
|
|
12
|
+
return value.join(", ");
|
|
13
|
+
}
|
|
14
|
+
return String(value);
|
|
15
|
+
}
|
|
16
|
+
var BEZIER = /^cubic-bezier\(\s*([^)]*)\)$/i;
|
|
17
|
+
function parseTokenValue(type, text) {
|
|
18
|
+
const raw = text.trim();
|
|
19
|
+
if (type === "cubicBezier") {
|
|
20
|
+
const inner = BEZIER.exec(raw)?.[1] ?? raw;
|
|
21
|
+
const parts = inner.split(",").map((p) => Number(p.trim()));
|
|
22
|
+
return parts.length === 4 && parts.every((n) => Number.isFinite(n)) ? parts : raw;
|
|
23
|
+
}
|
|
24
|
+
if (type === "fontFamily") {
|
|
25
|
+
const families = raw.split(",").map((f) => f.trim().replace(/^['"]|['"]$/g, "").trim()).filter((f) => f !== "");
|
|
26
|
+
return families.length > 0 ? families : raw;
|
|
27
|
+
}
|
|
28
|
+
return raw;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/chainTokens.ts
|
|
32
|
+
function localSelections(chain, selection) {
|
|
33
|
+
let axes = {};
|
|
34
|
+
return chain.map((link) => {
|
|
35
|
+
axes = mergeAxes(axes, link.axes);
|
|
36
|
+
return { ...selection, ...fullSelection(axes, selection) };
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function tokenAt(chain, name, selection) {
|
|
40
|
+
const local = localSelections(chain, selection);
|
|
41
|
+
for (let i = chain.length - 1; i >= 0; i--) {
|
|
42
|
+
const { tokens } = chain[i];
|
|
43
|
+
if (!Object.hasOwn(tokens, name)) continue;
|
|
44
|
+
const picked = pick(tokens[name], local[i]);
|
|
45
|
+
if (picked.ok) return picked.value;
|
|
46
|
+
}
|
|
47
|
+
return void 0;
|
|
48
|
+
}
|
|
49
|
+
function tokensAt(chain, selection) {
|
|
50
|
+
const local = localSelections(chain, selection);
|
|
51
|
+
const out = {};
|
|
52
|
+
chain.forEach((link, i) => {
|
|
53
|
+
for (const [name, v] of Object.entries(link.tokens)) {
|
|
54
|
+
const picked = pick(v, local[i]);
|
|
55
|
+
if (picked.ok) out[name] = picked.value;
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
function withAlpha(color, alpha) {
|
|
61
|
+
const rgba = parseRgba(color.trim());
|
|
62
|
+
if (!rgba) return mixAlpha(color.trim(), alpha);
|
|
63
|
+
const [r, g, b, a] = rgba;
|
|
64
|
+
return `rgba(${r}, ${g}, ${b}, ${round(a * alpha, 3)})`;
|
|
65
|
+
}
|
|
66
|
+
function mixAlpha(color, alpha) {
|
|
67
|
+
return `color-mix(in srgb, ${color} ${round(alpha * 100, 4)}%, transparent)`;
|
|
68
|
+
}
|
|
69
|
+
function atAlpha(color, alpha) {
|
|
70
|
+
const rgba = parseRgba(color.trim());
|
|
71
|
+
if (!rgba) return `rgb(from ${color.trim()} r g b / ${alpha})`;
|
|
72
|
+
const [r, g, b] = rgba;
|
|
73
|
+
return `rgba(${r}, ${g}, ${b}, ${round(alpha, 3)})`;
|
|
74
|
+
}
|
|
75
|
+
function round(n, places) {
|
|
76
|
+
return Number(n.toFixed(places));
|
|
77
|
+
}
|
|
78
|
+
function parseRgba(s) {
|
|
79
|
+
if (s.toLowerCase() === "transparent") return [0, 0, 0, 0];
|
|
80
|
+
return parseHex(s) ?? parseRgbFn(s);
|
|
81
|
+
}
|
|
82
|
+
function parseHex(s) {
|
|
83
|
+
const bytes = parseHexBytes(s);
|
|
84
|
+
return bytes && [bytes[0], bytes[1], bytes[2], bytes[3] / 255];
|
|
85
|
+
}
|
|
86
|
+
function parseRgbFn(s) {
|
|
87
|
+
const m = /^rgba?\(([^()]*)\)$/i.exec(s);
|
|
88
|
+
if (!m) return null;
|
|
89
|
+
const [channels, alphaPart, extra] = m[1].split("/");
|
|
90
|
+
if (extra !== void 0) return null;
|
|
91
|
+
const parts = channels.trim().split(/\s*,\s*|\s+/);
|
|
92
|
+
if (alphaPart !== void 0) parts.push(alphaPart.trim());
|
|
93
|
+
if (parts.length !== 3 && parts.length !== 4) return null;
|
|
94
|
+
const channel = (p) => p.endsWith("%") ? Number.parseFloat(p) / 100 * 255 : Number(p);
|
|
95
|
+
const alpha = (p) => p.endsWith("%") ? Number.parseFloat(p) / 100 : Number(p);
|
|
96
|
+
const [r, g, b] = parts.slice(0, 3).map(channel);
|
|
97
|
+
const a = parts[3] === void 0 ? 1 : alpha(parts[3]);
|
|
98
|
+
if (![r, g, b, a].every(Number.isFinite)) return null;
|
|
99
|
+
return [Math.round(r), Math.round(g), Math.round(b), a];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// src/dtcg/resolve.ts
|
|
103
|
+
var REF = /^\{([^}]+)\}$/;
|
|
104
|
+
function refTarget(value) {
|
|
105
|
+
if (typeof value !== "string") return null;
|
|
106
|
+
const m = REF.exec(value.trim());
|
|
107
|
+
if (!m) return null;
|
|
108
|
+
const path = m[1];
|
|
109
|
+
const dot = path.indexOf(".");
|
|
110
|
+
return dot === -1 ? path : path.slice(dot + 1);
|
|
111
|
+
}
|
|
112
|
+
function resolveTokens(tokens) {
|
|
113
|
+
const out = {};
|
|
114
|
+
const inProgress = /* @__PURE__ */ new Set();
|
|
115
|
+
const resolveOne = (name) => {
|
|
116
|
+
const cached = out[name];
|
|
117
|
+
if (cached !== void 0) return cached;
|
|
118
|
+
const token = tokens[name];
|
|
119
|
+
if (!token) throw new Error(`Unknown token "${name}"`);
|
|
120
|
+
if (inProgress.has(name)) {
|
|
121
|
+
throw new Error(`Reference cycle at token "${name}" (${[...inProgress].join(" \u2192 ")})`);
|
|
122
|
+
}
|
|
123
|
+
inProgress.add(name);
|
|
124
|
+
const target = refTarget(token.value);
|
|
125
|
+
let value;
|
|
126
|
+
if (target === null) {
|
|
127
|
+
value = serializeTokenValue(token.type, token.value);
|
|
128
|
+
} else if (!tokens[target]) {
|
|
129
|
+
throw new Error(`Token "${name}" references "${target}", which is not defined`);
|
|
130
|
+
} else {
|
|
131
|
+
value = resolveOne(target);
|
|
132
|
+
}
|
|
133
|
+
if (token.alpha !== void 0) value = withAlpha(value, token.alpha);
|
|
134
|
+
inProgress.delete(name);
|
|
135
|
+
out[name] = value;
|
|
136
|
+
return value;
|
|
137
|
+
};
|
|
138
|
+
for (const name of Object.keys(tokens)) resolveOne(name);
|
|
139
|
+
return out;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// src/resolveTheme.ts
|
|
143
|
+
function themeChain(theme) {
|
|
144
|
+
const out = [];
|
|
145
|
+
for (let t = theme; t; t = t.extends) out.unshift(t);
|
|
146
|
+
return out;
|
|
147
|
+
}
|
|
148
|
+
function themeAxes(theme) {
|
|
149
|
+
return themeChain(theme).reduce((axes, t) => mergeAxes(axes, t.axes), {});
|
|
150
|
+
}
|
|
151
|
+
function resolveTheme(theme, selection = {}) {
|
|
152
|
+
const sel = fullSelection(themeAxes(theme), selection);
|
|
153
|
+
const out = {};
|
|
154
|
+
for (const [name, value] of Object.entries(resolveTokens(tokensAt(themeChain(theme), sel)))) out[`--wzl-${name}`] = value;
|
|
155
|
+
return out;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export { atAlpha, mixAlpha, parseTokenValue, resolveTheme, resolveTokens, serializeTokenValue, themeAxes, themeChain, tokenAt, tokensAt, withAlpha };
|
|
159
|
+
//# sourceMappingURL=chunk-32JKTYS2.js.map
|
|
160
|
+
//# sourceMappingURL=chunk-32JKTYS2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/dtcg/value.ts","../src/chainTokens.ts","../src/dtcg/color.ts","../src/dtcg/resolve.ts","../src/resolveTheme.ts"],"names":[],"mappings":";;;;AAOA,SAAS,UAAU,KAAA,EAA6C;AAC9D,EAAA,OAAO,MAAM,GAAA,CAAI,CAAC,MAAO,OAAO,CAAA,KAAM,YAAY,IAAA,CAAK,IAAA,CAAK,CAAC,CAAA,GAAI,CAAA,CAAA,EAAI,CAAC,CAAA,CAAA,CAAA,GAAM,MAAA,CAAO,CAAC,CAAE,CAAA,CAAE,KAAK,IAAI,CAAA;AACnG;AAOO,SAAS,mBAAA,CAAoB,MAAc,KAAA,EAA2B;AAC3E,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACxB,IAAA,IAAI,SAAS,aAAA,EAAe,OAAO,gBAAgB,KAAA,CAAM,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA,CAAA;AACnE,IAAA,IAAI,IAAA,KAAS,YAAA,EAAc,OAAO,SAAA,CAAU,KAAK,CAAA;AACjD,IAAA,OAAO,KAAA,CAAM,KAAK,IAAI,CAAA;AAAA,EACxB;AACA,EAAA,OAAO,OAAO,KAAK,CAAA;AACrB;AAEA,IAAM,MAAA,GAAS,+BAAA;AAOR,SAAS,eAAA,CAAgB,MAAc,IAAA,EAA0B;AACtE,EAAA,MAAM,GAAA,GAAM,KAAK,IAAA,EAAK;AACtB,EAAA,IAAI,SAAS,aAAA,EAAe;AAC1B,IAAA,MAAM,QAAQ,MAAA,CAAO,IAAA,CAAK,GAAG,CAAA,GAAI,CAAC,CAAA,IAAK,GAAA;AACvC,IAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,KAAA,CAAM,GAAG,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,KAAM,MAAA,CAAO,CAAA,CAAE,IAAA,EAAM,CAAC,CAAA;AAC1D,IAAA,OAAO,KAAA,CAAM,MAAA,KAAW,CAAA,IAAK,KAAA,CAAM,KAAA,CAAM,CAAC,CAAA,KAAM,MAAA,CAAO,QAAA,CAAS,CAAC,CAAC,CAAA,GAAI,KAAA,GAAQ,GAAA;AAAA,EAChF;AACA,EAAA,IAAI,SAAS,YAAA,EAAc;AACzB,IAAA,MAAM,QAAA,GAAW,IACd,KAAA,CAAM,GAAG,EACT,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,EAAK,CAAE,QAAQ,cAAA,EAAgB,EAAE,EAAE,IAAA,EAAM,EACtD,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,KAAM,EAAE,CAAA;AACzB,IAAA,OAAO,QAAA,CAAS,MAAA,GAAS,CAAA,GAAI,QAAA,GAAW,GAAA;AAAA,EAC1C;AACA,EAAA,OAAO,GAAA;AACT;;;AChCA,SAAS,eAAA,CAAgB,OAA6B,SAAA,EAAmC;AACvF,EAAA,IAAI,OAAiB,EAAC;AACtB,EAAA,OAAO,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,KAAS;AACzB,IAAA,IAAA,GAAO,SAAA,CAAU,IAAA,EAAM,IAAA,CAAK,IAAI,CAAA;AAChC,IAAA,OAAO,EAAE,GAAG,SAAA,EAAW,GAAG,aAAA,CAAc,IAAA,EAAM,SAAS,CAAA,EAAE;AAAA,EAC3D,CAAC,CAAA;AACH;AAGO,SAAS,OAAA,CAAQ,KAAA,EAA6B,IAAA,EAAc,SAAA,EAA4C;AAC7G,EAAA,MAAM,KAAA,GAAQ,eAAA,CAAgB,KAAA,EAAO,SAAS,CAAA;AAC9C,EAAA,KAAA,IAAS,IAAI,KAAA,CAAM,MAAA,GAAS,CAAA,EAAG,CAAA,IAAK,GAAG,CAAA,EAAA,EAAK;AAC1C,IAAA,MAAM,EAAE,MAAA,EAAO,GAAI,KAAA,CAAM,CAAC,CAAA;AAC1B,IAAA,IAAI,CAAC,MAAA,CAAO,MAAA,CAAO,MAAA,EAAQ,IAAI,CAAA,EAAG;AAClC,IAAA,MAAM,SAAS,IAAA,CAAK,MAAA,CAAO,IAAI,CAAA,EAAG,KAAA,CAAM,CAAC,CAAC,CAAA;AAC1C,IAAA,IAAI,MAAA,CAAO,EAAA,EAAI,OAAO,MAAA,CAAO,KAAA;AAAA,EAC/B;AACA,EAAA,OAAO,MAAA;AACT;AAGO,SAAS,QAAA,CAAS,OAA6B,SAAA,EAAkC;AACtF,EAAA,MAAM,KAAA,GAAQ,eAAA,CAAgB,KAAA,EAAO,SAAS,CAAA;AAC9C,EAAA,MAAM,MAAgC,EAAC;AACvC,EAAA,KAAA,CAAM,OAAA,CAAQ,CAAC,IAAA,EAAM,CAAA,KAAM;AACzB,IAAA,KAAA,MAAW,CAAC,MAAM,CAAC,CAAA,IAAK,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAA,EAAG;AACnD,MAAA,MAAM,MAAA,GAAS,IAAA,CAAK,CAAA,EAAG,KAAA,CAAM,CAAC,CAAC,CAAA;AAC/B,MAAA,IAAI,MAAA,CAAO,EAAA,EAAI,GAAA,CAAI,IAAI,IAAI,MAAA,CAAO,KAAA;AAAA,IACpC;AAAA,EACF,CAAC,CAAA;AACD,EAAA,OAAO,GAAA;AACT;ACnCO,SAAS,SAAA,CAAU,OAAe,KAAA,EAAuB;AAC9D,EAAA,MAAM,IAAA,GAAO,SAAA,CAAU,KAAA,CAAM,IAAA,EAAM,CAAA;AACnC,EAAA,IAAI,CAAC,IAAA,EAAM,OAAO,SAAS,KAAA,CAAM,IAAA,IAAQ,KAAK,CAAA;AAC9C,EAAA,MAAM,CAAC,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA,GAAI,IAAA;AACrB,EAAA,OAAO,CAAA,KAAA,EAAQ,CAAC,CAAA,EAAA,EAAK,CAAC,CAAA,EAAA,EAAK,CAAC,CAAA,EAAA,EAAK,KAAA,CAAM,CAAA,GAAI,KAAA,EAAO,CAAC,CAAC,CAAA,CAAA,CAAA;AACtD;AAGO,SAAS,QAAA,CAAS,OAAe,KAAA,EAAuB;AAC7D,EAAA,OAAO,sBAAsB,KAAK,CAAA,CAAA,EAAI,MAAM,KAAA,GAAQ,GAAA,EAAK,CAAC,CAAC,CAAA,eAAA,CAAA;AAC7D;AAGO,SAAS,OAAA,CAAQ,OAAe,KAAA,EAAuB;AAC5D,EAAA,MAAM,IAAA,GAAO,SAAA,CAAU,KAAA,CAAM,IAAA,EAAM,CAAA;AACnC,EAAA,IAAI,CAAC,MAAM,OAAO,CAAA,SAAA,EAAY,MAAM,IAAA,EAAM,YAAY,KAAK,CAAA,CAAA,CAAA;AAC3D,EAAA,MAAM,CAAC,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA,GAAI,IAAA;AAClB,EAAA,OAAO,CAAA,KAAA,EAAQ,CAAC,CAAA,EAAA,EAAK,CAAC,CAAA,EAAA,EAAK,CAAC,CAAA,EAAA,EAAK,KAAA,CAAM,KAAA,EAAO,CAAC,CAAC,CAAA,CAAA,CAAA;AAClD;AAIA,SAAS,KAAA,CAAM,GAAW,MAAA,EAAwB;AAChD,EAAA,OAAO,MAAA,CAAO,CAAA,CAAE,OAAA,CAAQ,MAAM,CAAC,CAAA;AACjC;AAEA,SAAS,UAAU,CAAA,EAAwB;AACzC,EAAA,IAAI,CAAA,CAAE,aAAY,KAAM,aAAA,SAAsB,CAAC,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA;AACzD,EAAA,OAAO,QAAA,CAAS,CAAC,CAAA,IAAK,UAAA,CAAW,CAAC,CAAA;AACpC;AAEA,SAAS,SAAS,CAAA,EAAwB;AACxC,EAAA,MAAM,KAAA,GAAQ,cAAc,CAAC,CAAA;AAC7B,EAAA,OAAO,KAAA,IAAS,CAAC,KAAA,CAAM,CAAC,GAAG,KAAA,CAAM,CAAC,CAAA,EAAG,KAAA,CAAM,CAAC,CAAA,EAAG,KAAA,CAAM,CAAC,IAAI,GAAG,CAAA;AAC/D;AAEA,SAAS,WAAW,CAAA,EAAwB;AAC1C,EAAA,MAAM,CAAA,GAAI,sBAAA,CAAuB,IAAA,CAAK,CAAC,CAAA;AACvC,EAAA,IAAI,CAAC,GAAG,OAAO,IAAA;AACf,EAAA,MAAM,CAAC,UAAU,SAAA,EAAW,KAAK,IAAI,CAAA,CAAE,CAAC,CAAA,CAAE,KAAA,CAAM,GAAG,CAAA;AACnD,EAAA,IAAI,KAAA,KAAU,QAAW,OAAO,IAAA;AAChC,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,IAAA,EAAK,CAAE,MAAM,aAAa,CAAA;AACjD,EAAA,IAAI,cAAc,MAAA,EAAW,KAAA,CAAM,IAAA,CAAK,SAAA,CAAU,MAAM,CAAA;AACxD,EAAA,IAAI,MAAM,MAAA,KAAW,CAAA,IAAK,KAAA,CAAM,MAAA,KAAW,GAAG,OAAO,IAAA;AAErD,EAAA,MAAM,OAAA,GAAU,CAAC,CAAA,KAAe,CAAA,CAAE,SAAS,GAAG,CAAA,GAAK,MAAA,CAAO,UAAA,CAAW,CAAC,CAAA,GAAI,GAAA,GAAO,GAAA,GAAM,OAAO,CAAC,CAAA;AAC/F,EAAA,MAAM,KAAA,GAAQ,CAAC,CAAA,KAAe,CAAA,CAAE,QAAA,CAAS,GAAG,CAAA,GAAI,MAAA,CAAO,UAAA,CAAW,CAAC,CAAA,GAAI,GAAA,GAAM,OAAO,CAAC,CAAA;AACrF,EAAA,MAAM,CAAC,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA,GAAI,KAAA,CAAM,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA,CAAE,GAAA,CAAI,OAAO,CAAA;AAC/C,EAAA,MAAM,CAAA,GAAI,MAAM,CAAC,CAAA,KAAM,SAAY,CAAA,GAAI,KAAA,CAAM,KAAA,CAAM,CAAC,CAAC,CAAA;AACrD,EAAA,IAAI,CAAC,CAAC,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA,CAAE,KAAA,CAAM,MAAA,CAAO,QAAQ,CAAA,EAAG,OAAO,IAAA;AACjD,EAAA,OAAO,CAAC,IAAA,CAAK,KAAA,CAAM,CAAC,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,CAAC,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,CAAC,GAAG,CAAC,CAAA;AACxD;;;ACzDA,IAAM,GAAA,GAAM,eAAA;AAEZ,SAAS,UAAU,KAAA,EAAkC;AACnD,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,EAAU,OAAO,IAAA;AACtC,EAAA,MAAM,CAAA,GAAI,GAAA,CAAI,IAAA,CAAK,KAAA,CAAM,MAAM,CAAA;AAC/B,EAAA,IAAI,CAAC,GAAG,OAAO,IAAA;AACf,EAAA,MAAM,IAAA,GAAO,EAAE,CAAC,CAAA;AAChB,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,OAAA,CAAQ,GAAG,CAAA;AAC5B,EAAA,OAAO,QAAQ,EAAA,GAAK,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,MAAM,CAAC,CAAA;AAC/C;AAQO,SAAS,cAAc,MAAA,EAAsC;AAClE,EAAA,MAAM,MAAwB,EAAC;AAC/B,EAAA,MAAM,UAAA,uBAAiB,GAAA,EAAY;AAEnC,EAAA,MAAM,UAAA,GAAa,CAAC,IAAA,KAAyB;AAC3C,IAAA,MAAM,MAAA,GAAS,IAAI,IAAI,CAAA;AACvB,IAAA,IAAI,MAAA,KAAW,QAAW,OAAO,MAAA;AAEjC,IAAA,MAAM,KAAA,GAAQ,OAAO,IAAI,CAAA;AACzB,IAAA,IAAI,CAAC,KAAA,EAAO,MAAM,IAAI,KAAA,CAAM,CAAA,eAAA,EAAkB,IAAI,CAAA,CAAA,CAAG,CAAA;AAErD,IAAA,IAAI,UAAA,CAAW,GAAA,CAAI,IAAI,CAAA,EAAG;AACxB,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,0BAAA,EAA6B,IAAI,CAAA,GAAA,EAAM,CAAC,GAAG,UAAU,CAAA,CAAE,IAAA,CAAK,UAAK,CAAC,CAAA,CAAA,CAAG,CAAA;AAAA,IACvF;AACA,IAAA,UAAA,CAAW,IAAI,IAAI,CAAA;AAEnB,IAAA,MAAM,MAAA,GAAS,SAAA,CAAU,KAAA,CAAM,KAAK,CAAA;AACpC,IAAA,IAAI,KAAA;AACJ,IAAA,IAAI,WAAW,IAAA,EAAM;AACnB,MAAA,KAAA,GAAQ,mBAAA,CAAoB,KAAA,CAAM,IAAA,EAAM,KAAA,CAAM,KAAK,CAAA;AAAA,IACrD,CAAA,MAAA,IAAW,CAAC,MAAA,CAAO,MAAM,CAAA,EAAG;AAC1B,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,OAAA,EAAU,IAAI,CAAA,cAAA,EAAiB,MAAM,CAAA,uBAAA,CAAyB,CAAA;AAAA,IAChF,CAAA,MAAO;AACL,MAAA,KAAA,GAAQ,WAAW,MAAM,CAAA;AAAA,IAC3B;AAEA,IAAA,IAAI,MAAM,KAAA,KAAU,MAAA,UAAmB,SAAA,CAAU,KAAA,EAAO,MAAM,KAAK,CAAA;AAEnE,IAAA,UAAA,CAAW,OAAO,IAAI,CAAA;AACtB,IAAA,GAAA,CAAI,IAAI,CAAA,GAAI,KAAA;AACZ,IAAA,OAAO,KAAA;AAAA,EACT,CAAA;AAEA,EAAA,KAAA,MAAW,QAAQ,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA,aAAc,IAAI,CAAA;AACvD,EAAA,OAAO,GAAA;AACT;;;AC9CO,SAAS,WAAW,KAAA,EAAuB;AAChD,EAAA,MAAM,MAAe,EAAC;AACtB,EAAA,KAAA,IAAS,CAAA,GAAkB,OAAO,CAAA,EAAG,CAAA,GAAI,EAAE,OAAA,EAAS,GAAA,CAAI,QAAQ,CAAC,CAAA;AACjE,EAAA,OAAO,GAAA;AACT;AAGO,SAAS,UAAU,KAAA,EAAwB;AAChD,EAAA,OAAO,UAAA,CAAW,KAAK,CAAA,CAAE,MAAA,CAAiB,CAAC,IAAA,EAAM,CAAA,KAAM,SAAA,CAAU,IAAA,EAAM,CAAA,CAAE,IAAI,CAAA,EAAG,EAAE,CAAA;AACpF;AAUO,SAAS,YAAA,CAAa,KAAA,EAAc,SAAA,GAAuB,EAAC,EAAkB;AACnF,EAAA,MAAM,GAAA,GAAM,aAAA,CAAc,SAAA,CAAU,KAAK,GAAG,SAAS,CAAA;AACrD,EAAA,MAAM,MAA8B,EAAC;AACrC,EAAA,KAAA,MAAW,CAAC,IAAA,EAAM,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,cAAc,QAAA,CAAS,UAAA,CAAW,KAAK,CAAA,EAAG,GAAG,CAAC,CAAC,CAAA,MAAO,CAAA,MAAA,EAAS,IAAI,EAAE,CAAA,GAAI,KAAA;AACpH,EAAA,OAAO,GAAA;AACT","file":"chunk-32JKTYS2.js","sourcesContent":["import type { TokenValue } from './types.ts';\n\n/**\n * Quote family names containing whitespace; leave everything else bare.\n * Generic keywords (`sans-serif`) and hyphen-prefixed identifiers\n * (`-apple-system`) break if quoted, and neither contains a space.\n */\nfunction fontStack(value: readonly (string | number)[]): string {\n return value.map((f) => (typeof f === 'string' && /\\s/.test(f) ? `'${f}'` : String(f))).join(', ');\n}\n\n/**\n * A token's value as CSS renders it. A DTCG value is not always a string: a\n * `fontFamily` is a list of names and a `cubicBezier` is four numbers, and both\n * have a spelling that belongs in a stylesheet rather than in JSON.\n */\nexport function serializeTokenValue(type: string, value: TokenValue): string {\n if (Array.isArray(value)) {\n if (type === 'cubicBezier') return `cubic-bezier(${value.join(', ')})`;\n if (type === 'fontFamily') return fontStack(value);\n return value.join(', ');\n }\n return String(value);\n}\n\nconst BEZIER = /^cubic-bezier\\(\\s*([^)]*)\\)$/i;\n\n/**\n * The inverse of {@link serializeTokenValue}: text a person edited, back in the\n * shape a definition stores. Text that does not parse for its type is kept as\n * text rather than thrown away, so a half-typed value stays editable.\n */\nexport function parseTokenValue(type: string, text: string): TokenValue {\n const raw = text.trim();\n if (type === 'cubicBezier') {\n const inner = BEZIER.exec(raw)?.[1] ?? raw;\n const parts = inner.split(',').map((p) => Number(p.trim()));\n return parts.length === 4 && parts.every((n) => Number.isFinite(n)) ? parts : raw;\n }\n if (type === 'fontFamily') {\n const families = raw\n .split(',')\n .map((f) => f.trim().replace(/^['\"]|['\"]$/g, '').trim())\n .filter((f) => f !== '');\n return families.length > 0 ? families : raw;\n }\n return raw;\n}\n","import { fullSelection, mergeAxes, pick, type AxisDefs, type Selection, type Varying } from './axes.ts';\nimport type { FlatTokens, RawToken } from './dtcg/types.ts';\n\n/** One theme of an `extends` chain, as far as choosing its tokens goes. */\nexport interface ChainLink {\n readonly axes: AxisDefs;\n readonly tokens: Readonly<Record<string, Varying<RawToken>>>;\n}\n\n/**\n * The selection each theme of a root-first chain reads its own tokens at. An axis value a theme and\n * everything it extends never declared (one a descendant added) becomes that theme's default for the\n * axis, which is what the stylesheet's cascade gives it: no block of the ancestor matches the new\n * value, so its default declarations show through.\n */\nfunction localSelections(chain: readonly ChainLink[], selection: Selection): Selection[] {\n let axes: AxisDefs = {};\n return chain.map((link) => {\n axes = mergeAxes(axes, link.axes);\n return { ...selection, ...fullSelection(axes, selection) };\n });\n}\n\n/** `name` from the nearest theme of a root-first chain that has it at `selection`. */\nexport function tokenAt(chain: readonly ChainLink[], name: string, selection: Selection): RawToken | undefined {\n const local = localSelections(chain, selection);\n for (let i = chain.length - 1; i >= 0; i--) {\n const { tokens } = chain[i];\n if (!Object.hasOwn(tokens, name)) continue;\n const picked = pick(tokens[name], local[i]);\n if (picked.ok) return picked.value;\n }\n return undefined;\n}\n\n/** Every token of a root-first chain at `selection`, each from the nearest theme that has it there. */\nexport function tokensAt(chain: readonly ChainLink[], selection: Selection): FlatTokens {\n const local = localSelections(chain, selection);\n const out: Record<string, RawToken> = {};\n chain.forEach((link, i) => {\n for (const [name, v] of Object.entries(link.tokens)) {\n const picked = pick(v, local[i]);\n if (picked.ok) out[name] = picked.value;\n }\n });\n return out;\n}\n","import { parseHexBytes } from '@weasel-js/paint';\n\n/**\n * A resolved color at `alpha`, multiplied into any alpha it already carries. The\n * JS side of the alpha extension; the CSS side emits `color-mix()` instead so DOM\n * chrome keeps tracking a downstream override of the referenced token.\n *\n * Hex and `rgb()`/`rgba()` become an `rgba()` literal. Anything else — a named\n * color, `hsl()`, `oklch()`, a `var()` — becomes the same `color-mix()` the CSS\n * side writes, which the browser resolves.\n */\nexport function withAlpha(color: string, alpha: number): string {\n const rgba = parseRgba(color.trim());\n if (!rgba) return mixAlpha(color.trim(), alpha);\n const [r, g, b, a] = rgba;\n return `rgba(${r}, ${g}, ${b}, ${round(a * alpha, 3)})`;\n}\n\n/** `color` at `alpha` as a `color-mix()` with transparent, for a color only the browser can resolve. */\nexport function mixAlpha(color: string, alpha: number): string {\n return `color-mix(in srgb, ${color} ${round(alpha * 100, 4)}%, transparent)`;\n}\n\n/** A resolved color with its alpha replaced by `alpha`, as `rgb(from c r g b / alpha)` computes it. */\nexport function atAlpha(color: string, alpha: number): string {\n const rgba = parseRgba(color.trim());\n if (!rgba) return `rgb(from ${color.trim()} r g b / ${alpha})`;\n const [r, g, b] = rgba;\n return `rgba(${r}, ${g}, ${b}, ${round(alpha, 3)})`;\n}\n\ntype Rgba = [number, number, number, number];\n\nfunction round(n: number, places: number): number {\n return Number(n.toFixed(places));\n}\n\nfunction parseRgba(s: string): Rgba | null {\n if (s.toLowerCase() === 'transparent') return [0, 0, 0, 0];\n return parseHex(s) ?? parseRgbFn(s);\n}\n\nfunction parseHex(s: string): Rgba | null {\n const bytes = parseHexBytes(s);\n return bytes && [bytes[0], bytes[1], bytes[2], bytes[3] / 255];\n}\n\nfunction parseRgbFn(s: string): Rgba | null {\n const m = /^rgba?\\(([^()]*)\\)$/i.exec(s);\n if (!m) return null;\n const [channels, alphaPart, extra] = m[1].split('/');\n if (extra !== undefined) return null;\n const parts = channels.trim().split(/\\s*,\\s*|\\s+/);\n if (alphaPart !== undefined) parts.push(alphaPart.trim());\n if (parts.length !== 3 && parts.length !== 4) return null;\n\n const channel = (p: string) => (p.endsWith('%') ? (Number.parseFloat(p) / 100) * 255 : Number(p));\n const alpha = (p: string) => (p.endsWith('%') ? Number.parseFloat(p) / 100 : Number(p));\n const [r, g, b] = parts.slice(0, 3).map(channel);\n const a = parts[3] === undefined ? 1 : alpha(parts[3]);\n if (![r, g, b, a].every(Number.isFinite)) return null;\n return [Math.round(r), Math.round(g), Math.round(b), a];\n}\n","import { withAlpha } from './color.ts';\nimport type { FlatTokens, ResolvedTokenMap, TokenValue } from './types.ts';\nimport { serializeTokenValue } from './value.ts';\n\n/** `{color.gray-100}` → `gray-100`. The type group is dropped, per the naming rule. */\nconst REF = /^\\{([^}]+)\\}$/;\n\nfunction refTarget(value: TokenValue): string | null {\n if (typeof value !== 'string') return null;\n const m = REF.exec(value.trim());\n if (!m) return null;\n const path = m[1];\n const dot = path.indexOf('.');\n return dot === -1 ? path : path.slice(dot + 1);\n}\n\n/**\n * Resolve flat tokens to final CSS-ready strings.\n *\n * Merge modes before calling: this function has no notion of modes, only of a\n * complete token set.\n */\nexport function resolveTokens(tokens: FlatTokens): ResolvedTokenMap {\n const out: ResolvedTokenMap = {};\n const inProgress = new Set<string>();\n\n const resolveOne = (name: string): string => {\n const cached = out[name];\n if (cached !== undefined) return cached;\n\n const token = tokens[name];\n if (!token) throw new Error(`Unknown token \"${name}\"`);\n\n if (inProgress.has(name)) {\n throw new Error(`Reference cycle at token \"${name}\" (${[...inProgress].join(' → ')})`);\n }\n inProgress.add(name);\n\n const target = refTarget(token.value);\n let value: string;\n if (target === null) {\n value = serializeTokenValue(token.type, token.value);\n } else if (!tokens[target]) {\n throw new Error(`Token \"${name}\" references \"${target}\", which is not defined`);\n } else {\n value = resolveOne(target);\n }\n\n if (token.alpha !== undefined) value = withAlpha(value, token.alpha);\n\n inProgress.delete(name);\n out[name] = value;\n return value;\n };\n\n for (const name of Object.keys(tokens)) resolveOne(name);\n return out;\n}\n\n/** Merge a mode's tokens over the mode-invariant set. Later wins. */\nexport function mergeTokens(base: FlatTokens, mode: FlatTokens): FlatTokens {\n return { ...base, ...mode };\n}\n","import { fullSelection, mergeAxes, type AxisDefs, type Selection } from './axes.ts';\nimport { tokensAt } from './chainTokens.ts';\nimport { resolveTokens } from './dtcg/resolve.ts';\nimport type { TokenName } from './generated/themes.ts';\nimport type { Theme } from './theme.ts';\n\n/** The output of `resolveTheme`: every token of a theme, for one selection, keyed\n * by CSS custom-property name and flattened to a final CSS value. */\nexport type ResolvedTheme = Readonly<Record<TokenName, string>>;\n\n/** Root-first, so the leaf theme's tokens land last. */\nexport function themeChain(theme: Theme): Theme[] {\n const out: Theme[] = [];\n for (let t: Theme | null = theme; t; t = t.extends) out.unshift(t);\n return out;\n}\n\n/** Every axis the chain declares. An axis declared more than once keeps every value; a descendant's value entries and default win. */\nexport function themeAxes(theme: Theme): AxisDefs {\n return themeChain(theme).reduce<AxisDefs>((axes, t) => mergeAxes(axes, t.axes), {});\n}\n\n/**\n * Merge the extends chain at a selection, resolve every alias, and key the\n * result by CSS custom-property name. A missing or unknown axis value takes the\n * axis default, and a theme in the chain reads a value it never declared at its\n * own default for that axis.\n *\n * Pure — no DOM. An unresolvable reference throws rather than falling back.\n */\nexport function resolveTheme(theme: Theme, selection: Selection = {}): ResolvedTheme {\n const sel = fullSelection(themeAxes(theme), selection);\n const out: Record<string, string> = {};\n for (const [name, value] of Object.entries(resolveTokens(tokensAt(themeChain(theme), sel)))) out[`--wzl-${name}`] = value;\n return out as ResolvedTheme;\n}\n"]}
|