@weasel-js/theme 1.10.1 → 1.11.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/dist/index.d.ts CHANGED
@@ -99,6 +99,26 @@ declare function applyTheme(el: HTMLElement, theme: Theme, selection?: Selection
99
99
  */
100
100
  declare function resolveCurrentColor(value: string, current: string): string;
101
101
 
102
+ /** One layer of a CSS `box-shadow`, lengths in px. */
103
+ interface BoxShadow {
104
+ inset: boolean;
105
+ x: number;
106
+ y: number;
107
+ blur: number;
108
+ spread: number;
109
+ /** As written; `currentColor` when the layer names none. */
110
+ color: string;
111
+ }
112
+ /**
113
+ * `value` with every `var(--name)` and `var(--name, fallback)` replaced as a browser would: by
114
+ * `tokens[name]`, expanded in turn, or by the fallback when the theme has no such token. A
115
+ * resolved theme can hold references (a theme's bevel names its bevel colors), and a canvas
116
+ * reading one has no cascade to substitute them. Throws on a reference with neither.
117
+ */
118
+ declare function expandVars(value: string, tokens: Readonly<Record<string, string>>, seen?: readonly string[]): string;
119
+ /** A `box-shadow` value's layers, first (topmost) first; `none` has none. Lengths must be px or a bare 0. */
120
+ declare function parseBoxShadow(value: string): BoxShadow[];
121
+
102
122
  /** What `loadDTCG` reads: a `toDTCG` export, or a document shaped like one. */
103
123
  interface DtcgDocument {
104
124
  name?: unknown;
@@ -197,4 +217,4 @@ interface StanceLookup<S extends string> {
197
217
  */
198
218
  declare function resolveStanceSlots<S extends string>(tokens: Readonly<Record<string, string>>, base: Readonly<Record<S, string>>, { stance, nested, stanced }?: StanceLookup<S>): Record<S, string>;
199
219
 
200
- export { type AxisWeights, ColorList, ColorSourceContext, type DtcgDocument, ResolvedTheme, STANCES, STANCE_SLOTS, Selection, type Stance, type StanceLookup, type StanceSlot, TOKEN_MANIFEST, TONE_REF, Theme, type TokenManifestEntry, TokenName, TokenValue, applyTheme, blendTheme, colorAt, colorCssAt, colorSourceCss, colorSourceLiteral, loadDTCG, parseTokenValue, resolveCurrentColor, resolveStanceSlots, serializeTokenValue, stanceSlotNames, tokenPx };
220
+ export { type AxisWeights, type BoxShadow, ColorList, ColorSourceContext, type DtcgDocument, ResolvedTheme, STANCES, STANCE_SLOTS, Selection, type Stance, type StanceLookup, type StanceSlot, TOKEN_MANIFEST, TONE_REF, Theme, type TokenManifestEntry, TokenName, TokenValue, applyTheme, blendTheme, colorAt, colorCssAt, colorSourceCss, colorSourceLiteral, expandVars, loadDTCG, parseBoxShadow, parseTokenValue, resolveCurrentColor, resolveStanceSlots, serializeTokenValue, stanceSlotNames, tokenPx };
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
- export { TONE_REF, applyTheme, blendTheme, colorAt, colorCount, colorCssAt, colorSourceCss, colorSourceLiteral, isColorModePreference, rampSteps, resolveCurrentColor, themeTones } from './chunk-5PFIA5JU.js';
1
+ export { TONE_REF, applyTheme, blendTheme, colorAt, colorCount, colorCssAt, colorSourceCss, colorSourceLiteral, isColorModePreference, rampSteps, resolveCurrentColor, themeTones } from './chunk-QGOGBCYW.js';
2
2
  import { AXES_EXT, overrideKey, ALPHA_EXT } from './chunk-Z675UQ26.js';
3
3
  import './chunk-S7MJPFDP.js';
4
- import { TOKEN_MANIFEST } from './chunk-354G4TUL.js';
5
- export { TOKEN_MANIFEST } from './chunk-354G4TUL.js';
6
- import './chunk-M3R5MV7Q.js';
4
+ import { TOKEN_MANIFEST } from './chunk-GE2WCTOV.js';
5
+ export { TOKEN_MANIFEST } from './chunk-GE2WCTOV.js';
6
+ import { readCssNumber } from './chunk-M3R5MV7Q.js';
7
7
  import { themeAxes } from './chunk-32JKTYS2.js';
8
8
  export { parseTokenValue, resolveTheme, serializeTokenValue, themeAxes } from './chunk-32JKTYS2.js';
9
9
  import { weaselTheme } from './chunk-JX2OC444.js';
@@ -21,6 +21,76 @@ function tokenPx(name, resolved) {
21
21
  return Number(m[1]);
22
22
  }
23
23
 
24
+ // src/boxShadow.ts
25
+ function splitTop(css, sep) {
26
+ const out = [];
27
+ let depth = 0;
28
+ let start = 0;
29
+ for (let i = 0; i < css.length; i++) {
30
+ const ch = css[i];
31
+ if (ch === "(") depth++;
32
+ else if (ch === ")") depth--;
33
+ else if (depth === 0 && sep.test(ch)) {
34
+ out.push(css.slice(start, i));
35
+ start = i + 1;
36
+ }
37
+ }
38
+ out.push(css.slice(start));
39
+ return out.map((s) => s.trim()).filter((s) => s !== "");
40
+ }
41
+ function closing(css, open) {
42
+ let depth = 0;
43
+ for (let i = open; i < css.length; i++) {
44
+ if (css[i] === "(") depth++;
45
+ else if (css[i] === ")" && --depth === 0) return i;
46
+ }
47
+ return -1;
48
+ }
49
+ function expandVars(value, tokens, seen = []) {
50
+ let out = "";
51
+ let i = 0;
52
+ for (; ; ) {
53
+ const at = value.indexOf("var(", i);
54
+ if (at < 0) return out + value.slice(i);
55
+ const end = closing(value, at + 3);
56
+ if (end < 0) throw new Error(`expandVars: unclosed var() in "${value}"`);
57
+ const inner = value.slice(at + 4, end);
58
+ const comma = inner.indexOf(",");
59
+ const name = (comma < 0 ? inner : inner.slice(0, comma)).trim();
60
+ const fallback = comma < 0 ? void 0 : inner.slice(comma + 1).trim();
61
+ if (seen.includes(name)) throw new Error(`expandVars: ${name} refers to itself`);
62
+ const own = tokens[name];
63
+ const raw = own ?? fallback;
64
+ if (raw === void 0) throw new Error(`expandVars: ${name} is not in the theme and has no fallback`);
65
+ out += value.slice(i, at) + expandVars(raw, tokens, own === void 0 ? seen : [...seen, name]);
66
+ i = end + 1;
67
+ }
68
+ }
69
+ function parseBoxShadow(value) {
70
+ if (value.trim() === "none") return [];
71
+ return splitTop(value, /,/).map((layer) => {
72
+ let inset = false;
73
+ const lengths = [];
74
+ const color = [];
75
+ for (const word of splitTop(layer, /\s/)) {
76
+ if (word === "inset") {
77
+ inset = true;
78
+ continue;
79
+ }
80
+ const n = readCssNumber(word);
81
+ if (n && (n.unit === "px" || n.unit === "" && n.value === 0)) {
82
+ lengths.push(n.value);
83
+ continue;
84
+ }
85
+ if (n) throw new Error(`parseBoxShadow: ${word} is not a px length`);
86
+ color.push(word);
87
+ }
88
+ if (lengths.length < 2 || lengths.length > 4) throw new Error(`parseBoxShadow: cannot read "${layer}"`);
89
+ const [x, y, blur = 0, spread = 0] = lengths;
90
+ return { inset, x, y, blur, spread, color: color.length ? color.join(" ") : "currentColor" };
91
+ });
92
+ }
93
+
24
94
  // src/dtcg/flatten.ts
25
95
  var isToken = (v) => typeof v === "object" && v !== null && "$value" in v;
26
96
  function flattenTokens(doc) {
@@ -170,6 +240,6 @@ function resolveStanceSlots(tokens, base, { stance, nested, stanced } = {}) {
170
240
  return out;
171
241
  }
172
242
 
173
- export { STANCES, STANCE_SLOTS, loadDTCG, resolveStanceSlots, stanceSlotNames, tokenPx };
243
+ export { STANCES, STANCE_SLOTS, expandVars, loadDTCG, parseBoxShadow, resolveStanceSlots, stanceSlotNames, tokenPx };
174
244
  //# sourceMappingURL=index.js.map
175
245
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tokenPx.ts","../src/dtcg/flatten.ts","../src/loadDTCG.ts","../src/panel.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { TOKEN_MANIFEST } from './generated/manifest';\nimport type { ResolvedTheme } from './resolveTheme';\nimport type { TokenName } from './generated/themes';\n\nconst DEFAULTS = new Map<string, string>(TOKEN_MANIFEST.map((t) => [t.name, t.defaultValue]));\n\n/**\n * A px-valued token as a number, for code that draws rather than styles —\n * SVG geometry attributes, canvas, WebGL. Pass `resolved` (from\n * `useTheme().resolved`) where a live theme override must reach the drawing;\n * without it the built-in theme's value is used, which is static and costs no\n * style resolution.\n *\n * Throws rather than guessing: a token that is not a plain px length has no\n * number, and silently returning 0 collapses whatever it sized.\n */\nexport function tokenPx(name: TokenName, resolved?: ResolvedTheme): number {\n const raw = resolved?.[name] ?? DEFAULTS.get(name);\n const m = /^(-?\\d*\\.?\\d+)px$/.exec((raw ?? '').trim());\n if (!m) throw new Error(`tokenPx: ${name} is not a px length (${raw ?? 'unknown token'})`);\n return Number(m[1]);\n}\n","import { ALPHA_EXT, type FlatTokens, type RawToken, type TokenValue } from './types';\n\ninterface DtcgToken {\n $value: TokenValue;\n $type?: string;\n $description?: string;\n $extensions?: Record<string, unknown>;\n}\n\nconst isToken = (v: unknown): v is DtcgToken =>\n typeof v === 'object' && v !== null && '$value' in v;\n\n/**\n * Collapse a DTCG document into flat token names.\n *\n * Groups exist only to carry `$type`; they contribute nothing to the name, so\n * `color.fg-muted` becomes `fg-muted` and in turn `--wzl-fg-muted`. This shape\n * is forced by the token set: `--wzl-accent` and `--wzl-accent-base` are both\n * real tokens, and DTCG forbids a token that is also a group.\n */\nexport function flattenTokens(doc: Record<string, unknown>): FlatTokens {\n const out: FlatTokens = {};\n\n for (const [groupName, group] of Object.entries(doc)) {\n if (groupName.startsWith('$')) continue;\n if (typeof group !== 'object' || group === null) continue;\n\n const g = group as Record<string, unknown> & { $type?: string };\n const groupType = g.$type;\n\n for (const [leaf, node] of Object.entries(g)) {\n if (leaf.startsWith('$')) continue;\n if (!isToken(node)) continue;\n\n const type = node.$type ?? groupType;\n if (!type) {\n throw new Error(`Token \"${leaf}\" has no $type and its group \"${groupName}\" declares none`);\n }\n if (leaf in out) {\n throw new Error(`Duplicate token name \"${leaf}\" — leaf keys must be unique across type groups`);\n }\n\n const alphaRaw = node.$extensions?.[ALPHA_EXT];\n const token: RawToken = {\n type,\n value: node.$value,\n alpha: typeof alphaRaw === 'number' ? alphaRaw : undefined,\n description: node.$description,\n };\n out[leaf] = token;\n }\n }\n\n return out;\n}\n","import { isPlainObject } from '@weasel-js/registry';\nimport { mergeAxes, type AxisDef, type AxisDefs, type Selection, type Varying } from './axes';\nimport { AXES_EXT, overrideKey, type DtcgAxesExtension } from './dtcg/axesExtension';\nimport { flattenTokens } from './dtcg/flatten';\nimport type { FlatTokens, RawToken } from './dtcg/types';\nimport { themeAxes } from './resolveTheme';\nimport { weaselTheme, type Theme } from './theme';\n\n/** What `loadDTCG` reads: a `toDTCG` export, or a document shaped like one. */\nexport interface DtcgDocument {\n name?: unknown;\n defaultMode?: unknown;\n extends?: Theme | null;\n primitives?: Record<string, unknown>;\n modes?: Record<string, Record<string, unknown>>;\n $extensions?: Record<string, unknown>;\n}\n\ninterface Layer {\n readonly primitives: FlatTokens;\n readonly modes: Record<string, FlatTokens>;\n}\n\nconst isRecord = isPlainObject;\n\nfunction readLayer(src: { primitives?: unknown; modes?: unknown }): Layer {\n const modes = isRecord(src.modes) ? src.modes : {};\n return {\n primitives: flattenTokens(isRecord(src.primitives) ? src.primitives : {}),\n modes: Object.fromEntries(Object.entries(modes).map(([m, g]) => [m, flattenTokens(isRecord(g) ? g : {})])),\n };\n}\n\nfunction readAxesExtension(raw: unknown): DtcgAxesExtension | undefined {\n if (raw === undefined) return undefined;\n if (!isRecord(raw) || !isRecord(raw.axes)) throw new Error(`DTCG \"$extensions.${AXES_EXT}\" needs an \"axes\" object`);\n return {\n axes: raw.axes as AxisDefs,\n varies: (isRecord(raw.varies) ? raw.varies : {}) as DtcgAxesExtension['varies'],\n overrides: (isRecord(raw.overrides) ? raw.overrides : {}) as DtcgAxesExtension['overrides'],\n };\n}\n\n/**\n * Build a `Theme` from a DTCG document — the interchange path, for tokens\n * exported by a design tool rather than authored in TS. A token a mode leaves\n * out takes the document's primitive of the same name, or else the base theme's.\n * Axes besides mode come from `$extensions[\"com.weasel.axes\"]`, the encoding\n * `toDTCG` writes; a document without it loads with mode as its only axis.\n */\nexport function loadDTCG(doc: DtcgDocument): Theme {\n if (typeof doc.name !== 'string' || doc.name === '') {\n throw new Error('DTCG document needs a string \"name\"');\n }\n const base = doc.extends === undefined ? weaselTheme : doc.extends;\n const ext = readAxesExtension(doc.$extensions?.[AXES_EXT]);\n const plain = readLayer(doc);\n const overrides = Object.fromEntries(Object.entries(ext?.overrides ?? {}).map(([k, l]) => [k, readLayer(l)]));\n const modeNames = [...new Set([plain, ...Object.values(overrides)].flatMap((l) => Object.keys(l.modes)))];\n\n /** A token's value within one layer: varying by mode if any mode names it. */\n const tokenIn = (layer: Layer, name: string): Varying<RawToken> | undefined => {\n if (!modeNames.some((m) => layer.modes[m]?.[name])) return layer.primitives[name];\n const branch: Record<string, unknown> = { by: 'mode' };\n for (const m of modeNames) {\n const t = layer.modes[m]?.[name] ?? layer.primitives[name];\n if (t) branch[m] = t;\n }\n return branch as Varying<RawToken>;\n };\n\n const inherited = base ? themeAxes(base).mode?.default : undefined;\n const defaultMode = typeof doc.defaultMode === 'string' ? doc.defaultMode : (ext?.axes.mode?.default ?? inherited ?? 'dark');\n const modeAxis: AxisDef = {\n default: defaultMode,\n values: Object.fromEntries(modeNames.map((m) => [m, ext?.axes.mode?.values[m] ?? {}])),\n };\n const axes: Record<string, AxisDef> = {};\n for (const [name, def] of Object.entries(ext?.axes ?? { mode: modeAxis })) {\n if (name !== 'mode') axes[name] = def;\n else if (modeNames.length > 0) axes.mode = modeAxis;\n }\n if (modeNames.length > 0) axes.mode ??= modeAxis;\n const all = mergeAxes(base ? themeAxes(base) : {}, axes);\n\n /** Rebuild a token's tree over the axes it varies by, one override layer per leaf. */\n const nest = (name: string, vary: readonly string[], combo: Selection): Varying<RawToken> | undefined => {\n if (vary.length === 0) {\n const key = overrideKey(all, combo);\n const layer = key === '' ? plain : overrides[key];\n return layer && tokenIn(layer, name);\n }\n const [axis, ...rest] = vary;\n const out: Record<string, unknown> = { by: axis };\n let any = false;\n for (const value of Object.keys(all[axis].values)) {\n const v = nest(name, rest, { ...combo, [axis]: value });\n if (v !== undefined) {\n out[value] = v;\n any = true;\n }\n }\n return any ? (out as Varying<RawToken>) : undefined;\n };\n\n const tokens: Record<string, Varying<RawToken>> = {};\n const names = new Set([...Object.keys(plain.primitives), ...modeNames.flatMap((m) => Object.keys(plain.modes[m] ?? {}))]);\n for (const name of Object.keys(ext?.varies ?? {})) names.add(name);\n for (const name of names) {\n const vary = (ext?.varies[name] ?? []).filter((a) => a in all);\n const v = vary.length > 0 ? nest(name, vary, {}) : tokenIn(plain, name);\n if (v !== undefined) tokens[name] = v;\n }\n\n return { name: doc.name, extends: base, axes, tokens };\n}\n","/**\n * The vocabulary of stances, and the slots a theme styles a stanced surface with.\n *\n * A stance looks the same on every surface that takes one. A surface reads\n * `--wzl-stance-<stance>-<slot>` first — a nested panel\n * `--wzl-stance-<stance>-nested-<slot>` before that — and falls back to its\n * own base look, so a theme declares only the slots where a stance differs and\n * every other name here is read, with its fallback, and declared by nothing.\n * A panel's base look is itself theme slots, `--wzl-panel-<slot>`.\n */\n\n/** What kind of content a surface holds — a class of content, never a position. */\nexport const STANCES = ['scope', 'aside', 'advanced', 'debug', 'danger', 'notice', 'important', 'preview'] as const;\n\n/** One of {@link STANCES}. */\nexport type Stance = (typeof STANCES)[number];\n\n/** One styleable property of a stanced surface. */\nexport interface StanceSlot {\n readonly name: string;\n /** What it drives, for tooling. */\n readonly drives: string;\n}\n\n/** Every slot a theme can set per stance, as `--wzl-stance-<stance>-<slot>`. */\nexport const STANCE_SLOTS: readonly StanceSlot[] = [\n { name: 'surface', drives: 'background' },\n { name: 'border-color', drives: 'border-color' },\n { name: 'border-style', drives: 'border-style' },\n { name: 'border-width', drives: 'border-width' },\n { name: 'radius', drives: 'border-radius' },\n { name: 'pad', drives: 'padding' },\n { name: 'blur', drives: 'backdrop-filter' },\n { name: 'tone-mix', drives: 'how much of the tone mixes into the surface' },\n { name: 'tone', drives: 'the tone a surface takes when it names none' },\n // Only a surface that names its own tone recolors its controls.\n { name: 'accent', drives: 'the accent the controls inside a surface with a tone take; unset, the tone' },\n { name: 'title-font', drives: 'font-family' },\n { name: 'title-weight', drives: 'font-weight' },\n { name: 'title-size', drives: 'font-size' },\n { name: 'title-case', drives: 'text-transform' },\n { name: 'title-tracking', drives: 'letter-spacing' },\n { name: 'title-color', drives: 'color' },\n { name: 'title-inset', drives: 'the title’s padding' },\n];\n\n/** Every stance-slot name, `--wzl-`-prefixed, nested variants included. */\nexport function stanceSlotNames(): string[] {\n return STANCES.flatMap((stance) =>\n STANCE_SLOTS.flatMap((slot) => [`--wzl-stance-${stance}-${slot.name}`, `--wzl-stance-${stance}-nested-${slot.name}`]),\n );\n}\n\n/** What `resolveStanceSlots` resolves a surface against, besides its tokens. */\nexport interface StanceLookup<S extends string> {\n readonly stance?: Stance;\n /** The surface sits inside one of its own kind, and reads `-nested-<slot>` first. */\n readonly nested?: boolean;\n /** A stanced surface's fallback, where it differs from `base`. */\n readonly stanced?: Partial<Readonly<Record<S, string>>>;\n}\n\n/**\n * A stanced surface's look, for one drawn without the cascade — a canvas\n * surface reading a resolved theme. The same lookup the generated CSS makes:\n * `--wzl-stance-<stance>-<slot>`, then the stanced fallback, then `base`.\n * `base` holds final values, one per slot the surface draws.\n *\n * The tone is the caller's: a surface that names one mixes it into\n * `surface` at `tone-mix`; one that names none takes the `tone` slot, which\n * falls back to `surface` so mixing it changes nothing.\n */\nexport function resolveStanceSlots<S extends string>(\n tokens: Readonly<Record<string, string>>,\n base: Readonly<Record<S, string>>,\n { stance, nested, stanced }: StanceLookup<S> = {},\n): Record<S, string> {\n const out: Record<S, string> = { ...base };\n if (stance === undefined) return out;\n for (const slot of Object.keys(base) as S[]) {\n const own = tokens[`--wzl-stance-${stance}-${slot}`];\n const inner = nested ? tokens[`--wzl-stance-${stance}-nested-${slot}`] : undefined;\n // A stance's own tone falls back to the surface, never to the surface's base tone.\n const fallback = slot === 'tone' ? (base as Record<string, string>).surface ?? base[slot] : stanced?.[slot] ?? base[slot];\n out[slot] = inner ?? own ?? fallback;\n }\n return out;\n}\n"]}
1
+ {"version":3,"sources":["../src/tokenPx.ts","../src/boxShadow.ts","../src/dtcg/flatten.ts","../src/loadDTCG.ts","../src/panel.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { TOKEN_MANIFEST } from './generated/manifest';\nimport type { ResolvedTheme } from './resolveTheme';\nimport type { TokenName } from './generated/themes';\n\nconst DEFAULTS = new Map<string, string>(TOKEN_MANIFEST.map((t) => [t.name, t.defaultValue]));\n\n/**\n * A px-valued token as a number, for code that draws rather than styles —\n * SVG geometry attributes, canvas, WebGL. Pass `resolved` (from\n * `useTheme().resolved`) where a live theme override must reach the drawing;\n * without it the built-in theme's value is used, which is static and costs no\n * style resolution.\n *\n * Throws rather than guessing: a token that is not a plain px length has no\n * number, and silently returning 0 collapses whatever it sized.\n */\nexport function tokenPx(name: TokenName, resolved?: ResolvedTheme): number {\n const raw = resolved?.[name] ?? DEFAULTS.get(name);\n const m = /^(-?\\d*\\.?\\d+)px$/.exec((raw ?? '').trim());\n if (!m) throw new Error(`tokenPx: ${name} is not a px length (${raw ?? 'unknown token'})`);\n return Number(m[1]);\n}\n","import { readCssNumber } from './cssNumber';\n\n/** One layer of a CSS `box-shadow`, lengths in px. */\nexport interface BoxShadow {\n inset: boolean;\n x: number;\n y: number;\n blur: number;\n spread: number;\n /** As written; `currentColor` when the layer names none. */\n color: string;\n}\n\n/** `css` split at each top-level `sep`, ignoring any inside parentheses. */\nfunction splitTop(css: string, sep: RegExp): string[] {\n const out: string[] = [];\n let depth = 0;\n let start = 0;\n for (let i = 0; i < css.length; i++) {\n const ch = css[i]!;\n if (ch === '(') depth++;\n else if (ch === ')') depth--;\n else if (depth === 0 && sep.test(ch)) {\n out.push(css.slice(start, i));\n start = i + 1;\n }\n }\n out.push(css.slice(start));\n return out.map((s) => s.trim()).filter((s) => s !== '');\n}\n\n/** The index of the `)` closing the `(` at `open`, or -1. */\nfunction closing(css: string, open: number): number {\n let depth = 0;\n for (let i = open; i < css.length; i++) {\n if (css[i] === '(') depth++;\n else if (css[i] === ')' && --depth === 0) return i;\n }\n return -1;\n}\n\n/**\n * `value` with every `var(--name)` and `var(--name, fallback)` replaced as a browser would: by\n * `tokens[name]`, expanded in turn, or by the fallback when the theme has no such token. A\n * resolved theme can hold references (a theme's bevel names its bevel colors), and a canvas\n * reading one has no cascade to substitute them. Throws on a reference with neither.\n */\nexport function expandVars(value: string, tokens: Readonly<Record<string, string>>, seen: readonly string[] = []): string {\n let out = '';\n let i = 0;\n for (;;) {\n const at = value.indexOf('var(', i);\n if (at < 0) return out + value.slice(i);\n const end = closing(value, at + 3);\n if (end < 0) throw new Error(`expandVars: unclosed var() in \"${value}\"`);\n const inner = value.slice(at + 4, end);\n const comma = inner.indexOf(',');\n const name = (comma < 0 ? inner : inner.slice(0, comma)).trim();\n const fallback = comma < 0 ? undefined : inner.slice(comma + 1).trim();\n if (seen.includes(name)) throw new Error(`expandVars: ${name} refers to itself`);\n const own = tokens[name];\n const raw = own ?? fallback;\n if (raw === undefined) throw new Error(`expandVars: ${name} is not in the theme and has no fallback`);\n out += value.slice(i, at) + expandVars(raw, tokens, own === undefined ? seen : [...seen, name]);\n i = end + 1;\n }\n}\n\n/** A `box-shadow` value's layers, first (topmost) first; `none` has none. Lengths must be px or a bare 0. */\nexport function parseBoxShadow(value: string): BoxShadow[] {\n if (value.trim() === 'none') return [];\n return splitTop(value, /,/).map((layer) => {\n let inset = false;\n const lengths: number[] = [];\n const color: string[] = [];\n for (const word of splitTop(layer, /\\s/)) {\n if (word === 'inset') { inset = true; continue; }\n const n = readCssNumber(word);\n if (n && (n.unit === 'px' || (n.unit === '' && n.value === 0))) { lengths.push(n.value); continue; }\n if (n) throw new Error(`parseBoxShadow: ${word} is not a px length`);\n color.push(word);\n }\n if (lengths.length < 2 || lengths.length > 4) throw new Error(`parseBoxShadow: cannot read \"${layer}\"`);\n const [x, y, blur = 0, spread = 0] = lengths as [number, number, number?, number?];\n return { inset, x, y, blur, spread, color: color.length ? color.join(' ') : 'currentColor' };\n });\n}\n","import { ALPHA_EXT, type FlatTokens, type RawToken, type TokenValue } from './types';\n\ninterface DtcgToken {\n $value: TokenValue;\n $type?: string;\n $description?: string;\n $extensions?: Record<string, unknown>;\n}\n\nconst isToken = (v: unknown): v is DtcgToken =>\n typeof v === 'object' && v !== null && '$value' in v;\n\n/**\n * Collapse a DTCG document into flat token names.\n *\n * Groups exist only to carry `$type`; they contribute nothing to the name, so\n * `color.fg-muted` becomes `fg-muted` and in turn `--wzl-fg-muted`. This shape\n * is forced by the token set: `--wzl-accent` and `--wzl-accent-base` are both\n * real tokens, and DTCG forbids a token that is also a group.\n */\nexport function flattenTokens(doc: Record<string, unknown>): FlatTokens {\n const out: FlatTokens = {};\n\n for (const [groupName, group] of Object.entries(doc)) {\n if (groupName.startsWith('$')) continue;\n if (typeof group !== 'object' || group === null) continue;\n\n const g = group as Record<string, unknown> & { $type?: string };\n const groupType = g.$type;\n\n for (const [leaf, node] of Object.entries(g)) {\n if (leaf.startsWith('$')) continue;\n if (!isToken(node)) continue;\n\n const type = node.$type ?? groupType;\n if (!type) {\n throw new Error(`Token \"${leaf}\" has no $type and its group \"${groupName}\" declares none`);\n }\n if (leaf in out) {\n throw new Error(`Duplicate token name \"${leaf}\" — leaf keys must be unique across type groups`);\n }\n\n const alphaRaw = node.$extensions?.[ALPHA_EXT];\n const token: RawToken = {\n type,\n value: node.$value,\n alpha: typeof alphaRaw === 'number' ? alphaRaw : undefined,\n description: node.$description,\n };\n out[leaf] = token;\n }\n }\n\n return out;\n}\n","import { isPlainObject } from '@weasel-js/registry';\nimport { mergeAxes, type AxisDef, type AxisDefs, type Selection, type Varying } from './axes';\nimport { AXES_EXT, overrideKey, type DtcgAxesExtension } from './dtcg/axesExtension';\nimport { flattenTokens } from './dtcg/flatten';\nimport type { FlatTokens, RawToken } from './dtcg/types';\nimport { themeAxes } from './resolveTheme';\nimport { weaselTheme, type Theme } from './theme';\n\n/** What `loadDTCG` reads: a `toDTCG` export, or a document shaped like one. */\nexport interface DtcgDocument {\n name?: unknown;\n defaultMode?: unknown;\n extends?: Theme | null;\n primitives?: Record<string, unknown>;\n modes?: Record<string, Record<string, unknown>>;\n $extensions?: Record<string, unknown>;\n}\n\ninterface Layer {\n readonly primitives: FlatTokens;\n readonly modes: Record<string, FlatTokens>;\n}\n\nconst isRecord = isPlainObject;\n\nfunction readLayer(src: { primitives?: unknown; modes?: unknown }): Layer {\n const modes = isRecord(src.modes) ? src.modes : {};\n return {\n primitives: flattenTokens(isRecord(src.primitives) ? src.primitives : {}),\n modes: Object.fromEntries(Object.entries(modes).map(([m, g]) => [m, flattenTokens(isRecord(g) ? g : {})])),\n };\n}\n\nfunction readAxesExtension(raw: unknown): DtcgAxesExtension | undefined {\n if (raw === undefined) return undefined;\n if (!isRecord(raw) || !isRecord(raw.axes)) throw new Error(`DTCG \"$extensions.${AXES_EXT}\" needs an \"axes\" object`);\n return {\n axes: raw.axes as AxisDefs,\n varies: (isRecord(raw.varies) ? raw.varies : {}) as DtcgAxesExtension['varies'],\n overrides: (isRecord(raw.overrides) ? raw.overrides : {}) as DtcgAxesExtension['overrides'],\n };\n}\n\n/**\n * Build a `Theme` from a DTCG document — the interchange path, for tokens\n * exported by a design tool rather than authored in TS. A token a mode leaves\n * out takes the document's primitive of the same name, or else the base theme's.\n * Axes besides mode come from `$extensions[\"com.weasel.axes\"]`, the encoding\n * `toDTCG` writes; a document without it loads with mode as its only axis.\n */\nexport function loadDTCG(doc: DtcgDocument): Theme {\n if (typeof doc.name !== 'string' || doc.name === '') {\n throw new Error('DTCG document needs a string \"name\"');\n }\n const base = doc.extends === undefined ? weaselTheme : doc.extends;\n const ext = readAxesExtension(doc.$extensions?.[AXES_EXT]);\n const plain = readLayer(doc);\n const overrides = Object.fromEntries(Object.entries(ext?.overrides ?? {}).map(([k, l]) => [k, readLayer(l)]));\n const modeNames = [...new Set([plain, ...Object.values(overrides)].flatMap((l) => Object.keys(l.modes)))];\n\n /** A token's value within one layer: varying by mode if any mode names it. */\n const tokenIn = (layer: Layer, name: string): Varying<RawToken> | undefined => {\n if (!modeNames.some((m) => layer.modes[m]?.[name])) return layer.primitives[name];\n const branch: Record<string, unknown> = { by: 'mode' };\n for (const m of modeNames) {\n const t = layer.modes[m]?.[name] ?? layer.primitives[name];\n if (t) branch[m] = t;\n }\n return branch as Varying<RawToken>;\n };\n\n const inherited = base ? themeAxes(base).mode?.default : undefined;\n const defaultMode = typeof doc.defaultMode === 'string' ? doc.defaultMode : (ext?.axes.mode?.default ?? inherited ?? 'dark');\n const modeAxis: AxisDef = {\n default: defaultMode,\n values: Object.fromEntries(modeNames.map((m) => [m, ext?.axes.mode?.values[m] ?? {}])),\n };\n const axes: Record<string, AxisDef> = {};\n for (const [name, def] of Object.entries(ext?.axes ?? { mode: modeAxis })) {\n if (name !== 'mode') axes[name] = def;\n else if (modeNames.length > 0) axes.mode = modeAxis;\n }\n if (modeNames.length > 0) axes.mode ??= modeAxis;\n const all = mergeAxes(base ? themeAxes(base) : {}, axes);\n\n /** Rebuild a token's tree over the axes it varies by, one override layer per leaf. */\n const nest = (name: string, vary: readonly string[], combo: Selection): Varying<RawToken> | undefined => {\n if (vary.length === 0) {\n const key = overrideKey(all, combo);\n const layer = key === '' ? plain : overrides[key];\n return layer && tokenIn(layer, name);\n }\n const [axis, ...rest] = vary;\n const out: Record<string, unknown> = { by: axis };\n let any = false;\n for (const value of Object.keys(all[axis].values)) {\n const v = nest(name, rest, { ...combo, [axis]: value });\n if (v !== undefined) {\n out[value] = v;\n any = true;\n }\n }\n return any ? (out as Varying<RawToken>) : undefined;\n };\n\n const tokens: Record<string, Varying<RawToken>> = {};\n const names = new Set([...Object.keys(plain.primitives), ...modeNames.flatMap((m) => Object.keys(plain.modes[m] ?? {}))]);\n for (const name of Object.keys(ext?.varies ?? {})) names.add(name);\n for (const name of names) {\n const vary = (ext?.varies[name] ?? []).filter((a) => a in all);\n const v = vary.length > 0 ? nest(name, vary, {}) : tokenIn(plain, name);\n if (v !== undefined) tokens[name] = v;\n }\n\n return { name: doc.name, extends: base, axes, tokens };\n}\n","/**\n * The vocabulary of stances, and the slots a theme styles a stanced surface with.\n *\n * A stance looks the same on every surface that takes one. A surface reads\n * `--wzl-stance-<stance>-<slot>` first — a nested panel\n * `--wzl-stance-<stance>-nested-<slot>` before that — and falls back to its\n * own base look, so a theme declares only the slots where a stance differs and\n * every other name here is read, with its fallback, and declared by nothing.\n * A panel's base look is itself theme slots, `--wzl-panel-<slot>`.\n */\n\n/** What kind of content a surface holds — a class of content, never a position. */\nexport const STANCES = ['scope', 'aside', 'advanced', 'debug', 'danger', 'notice', 'important', 'preview'] as const;\n\n/** One of {@link STANCES}. */\nexport type Stance = (typeof STANCES)[number];\n\n/** One styleable property of a stanced surface. */\nexport interface StanceSlot {\n readonly name: string;\n /** What it drives, for tooling. */\n readonly drives: string;\n}\n\n/** Every slot a theme can set per stance, as `--wzl-stance-<stance>-<slot>`. */\nexport const STANCE_SLOTS: readonly StanceSlot[] = [\n { name: 'surface', drives: 'background' },\n { name: 'border-color', drives: 'border-color' },\n { name: 'border-style', drives: 'border-style' },\n { name: 'border-width', drives: 'border-width' },\n { name: 'radius', drives: 'border-radius' },\n { name: 'pad', drives: 'padding' },\n { name: 'blur', drives: 'backdrop-filter' },\n { name: 'tone-mix', drives: 'how much of the tone mixes into the surface' },\n { name: 'tone', drives: 'the tone a surface takes when it names none' },\n // Only a surface that names its own tone recolors its controls.\n { name: 'accent', drives: 'the accent the controls inside a surface with a tone take; unset, the tone' },\n { name: 'title-font', drives: 'font-family' },\n { name: 'title-weight', drives: 'font-weight' },\n { name: 'title-size', drives: 'font-size' },\n { name: 'title-case', drives: 'text-transform' },\n { name: 'title-tracking', drives: 'letter-spacing' },\n { name: 'title-color', drives: 'color' },\n { name: 'title-inset', drives: 'the title’s padding' },\n];\n\n/** Every stance-slot name, `--wzl-`-prefixed, nested variants included. */\nexport function stanceSlotNames(): string[] {\n return STANCES.flatMap((stance) =>\n STANCE_SLOTS.flatMap((slot) => [`--wzl-stance-${stance}-${slot.name}`, `--wzl-stance-${stance}-nested-${slot.name}`]),\n );\n}\n\n/** What `resolveStanceSlots` resolves a surface against, besides its tokens. */\nexport interface StanceLookup<S extends string> {\n readonly stance?: Stance;\n /** The surface sits inside one of its own kind, and reads `-nested-<slot>` first. */\n readonly nested?: boolean;\n /** A stanced surface's fallback, where it differs from `base`. */\n readonly stanced?: Partial<Readonly<Record<S, string>>>;\n}\n\n/**\n * A stanced surface's look, for one drawn without the cascade — a canvas\n * surface reading a resolved theme. The same lookup the generated CSS makes:\n * `--wzl-stance-<stance>-<slot>`, then the stanced fallback, then `base`.\n * `base` holds final values, one per slot the surface draws.\n *\n * The tone is the caller's: a surface that names one mixes it into\n * `surface` at `tone-mix`; one that names none takes the `tone` slot, which\n * falls back to `surface` so mixing it changes nothing.\n */\nexport function resolveStanceSlots<S extends string>(\n tokens: Readonly<Record<string, string>>,\n base: Readonly<Record<S, string>>,\n { stance, nested, stanced }: StanceLookup<S> = {},\n): Record<S, string> {\n const out: Record<S, string> = { ...base };\n if (stance === undefined) return out;\n for (const slot of Object.keys(base) as S[]) {\n const own = tokens[`--wzl-stance-${stance}-${slot}`];\n const inner = nested ? tokens[`--wzl-stance-${stance}-nested-${slot}`] : undefined;\n // A stance's own tone falls back to the surface, never to the surface's base tone.\n const fallback = slot === 'tone' ? (base as Record<string, string>).surface ?? base[slot] : stanced?.[slot] ?? base[slot];\n out[slot] = inner ?? own ?? fallback;\n }\n return out;\n}\n"]}
package/dist/prefs.js CHANGED
@@ -1,4 +1,4 @@
1
- import { TOKEN_MANIFEST } from './chunk-354G4TUL.js';
1
+ import { TOKEN_MANIFEST } from './chunk-GE2WCTOV.js';
2
2
  import { readCssNumber } from './chunk-M3R5MV7Q.js';
3
3
  import { themeAxes, resolveTheme, themeChain } from './chunk-32JKTYS2.js';
4
4
  import { weaselTheme, defineTheme } from './chunk-JX2OC444.js';
package/dist/react.d.ts CHANGED
@@ -50,7 +50,9 @@ declare const ThemeContext: React.Context<ThemeContextValue | null>;
50
50
  /** Props for `<ThemeProvider>`. */
51
51
  interface ThemeProviderProps {
52
52
  readonly theme?: Theme;
53
- /** Axis values, e.g. `{ mode: 'light' }`. A missing axis takes its default. */
53
+ /** Axis values, e.g. `{ mode: 'light' }`. A missing axis takes its default. `mode: 'auto'`
54
+ * follows the OS setting, live; any other value the theme lacks takes the default, with a
55
+ * warning in development. */
54
56
  readonly selection?: Selection;
55
57
  /** The colors a `tone` index picks from in this subtree. Absent: the theme's own. */
56
58
  readonly tones?: ColorList;
package/dist/react.js CHANGED
@@ -1,11 +1,12 @@
1
- import { isColorModePreference, themeTones, applyTheme, colorSourceCss } from './chunk-5PFIA5JU.js';
2
- export { isColorModePreference } from './chunk-5PFIA5JU.js';
1
+ import { isColorModePreference, themeTones, applyTheme, colorSourceCss } from './chunk-QGOGBCYW.js';
2
+ export { isColorModePreference } from './chunk-QGOGBCYW.js';
3
3
  import './chunk-S7MJPFDP.js';
4
4
  import './chunk-M3R5MV7Q.js';
5
5
  import { themeAxes, resolveTheme } from './chunk-32JKTYS2.js';
6
6
  import { weaselTheme } from './chunk-JX2OC444.js';
7
7
  import { fullSelection, selectionKey } from './chunk-P5F3GZV6.js';
8
8
  import { createContext, useState, useEffect, useCallback, useRef, useMemo, useLayoutEffect, useContext } from 'react';
9
+ import { isDev } from '@weasel-js/registry';
9
10
  import { jsx } from 'react/jsx-runtime';
10
11
 
11
12
  function systemQuery() {
@@ -70,7 +71,11 @@ function ThemeProvider({
70
71
  }) {
71
72
  const ref = useRef(null);
72
73
  const axes = themeAxes(theme);
73
- const full = fullSelection(axes, selection);
74
+ const autoMode = selection?.mode === "auto" && !("auto" in (axes.mode?.values ?? {}));
75
+ const systemMode2 = useResolvedColorMode(autoMode ? "auto" : "dark");
76
+ const given = autoMode ? { ...selection, mode: systemMode2 } : selection;
77
+ const full = fullSelection(axes, given);
78
+ warnUnknownAxisValues(theme, axes, given);
74
79
  const key = selectionKey(axes, full);
75
80
  const value = useMemo(
76
81
  () => ({ theme, selection: full, resolved: resolveTheme(theme, full), tones: tones ?? themeTones(theme), palette }),
@@ -85,6 +90,20 @@ function ThemeProvider({
85
90
  const attrs = Object.fromEntries(Object.keys(axes).map((a) => [`data-wzl-${a}`, full[a]]));
86
91
  return /* @__PURE__ */ jsx(ThemeContext.Provider, { value, children: /* @__PURE__ */ jsx("div", { ref, className, style, "data-wzl-theme": theme.name, ...attrs, children }) });
87
92
  }
93
+ var warned = /* @__PURE__ */ new Set();
94
+ function warnUnknownAxisValues(theme, axes, selection) {
95
+ if (!selection || !isDev()) return;
96
+ for (const [axis, value] of Object.entries(selection)) {
97
+ const def = axes[axis];
98
+ if (!def || value in def.values) continue;
99
+ const key = `${theme.name}\0${axis}\0${value}`;
100
+ if (warned.has(key)) continue;
101
+ warned.add(key);
102
+ console.warn(
103
+ `[weasel theme] ThemeProvider: theme "${theme.name}" has no ${axis} "${value}"; using "${def.default}". Its ${axis} values: ${Object.keys(def.values).join(", ")}.`
104
+ );
105
+ }
106
+ }
88
107
  function useTheme() {
89
108
  const ctx = useContext(ThemeContext);
90
109
  if (!ctx) throw new Error("useTheme must be used inside a <ThemeProvider>");
package/dist/react.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/useColorMode.ts","../src/react.tsx"],"names":["useCallback"],"mappings":"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","file":"react.js","sourcesContent":["import { useCallback, useEffect, useState } from 'react';\nimport { isColorModePreference, type ColorMode, type ColorModePreference } from './colorMode';\n\nfunction systemQuery(): MediaQueryList | null {\n if (typeof globalThis.matchMedia !== 'function') return null;\n return globalThis.matchMedia('(prefers-color-scheme: light)');\n}\n\nconst systemMode = (): ColorMode => (systemQuery()?.matches ? 'light' : 'dark');\n\n/**\n * Resolves a preference to the mode a `ThemeProvider` selection takes.\n * `'auto'` subscribes to the OS setting and follows it live; an explicit\n * choice opts out of the subscription entirely.\n */\nexport function useResolvedColorMode(preference: ColorModePreference): ColorMode {\n const [system, setSystem] = useState<ColorMode>(systemMode);\n\n useEffect(() => {\n if (preference !== 'auto') return;\n const mq = systemQuery();\n if (!mq) return;\n // eslint-disable-next-line react-hooks/set-state-in-effect -- syncs to the OS color-scheme query\n setSystem(mq.matches ? 'light' : 'dark');\n const onChange = (e: { matches: boolean }) => setSystem(e.matches ? 'light' : 'dark');\n mq.addEventListener('change', onChange);\n return () => mq.removeEventListener('change', onChange);\n }, [preference]);\n\n return preference === 'auto' ? system : preference;\n}\n\n/** The part of `Storage` {@link useColorModePreference} reads and writes. */\nexport type ColorModeStorage = Pick<Storage, 'getItem' | 'setItem'>;\n\n/** Options for {@link useColorModePreference}. */\nexport interface ColorModePreferenceOptions {\n /** Where the choice is remembered. Unset, it lasts as long as the component. */\n readonly storageKey?: string;\n /** The store `storageKey` names a slot in. Defaults to `localStorage`. */\n readonly storage?: ColorModeStorage;\n /** The preference before the user has chosen one. Defaults to `'auto'`. */\n readonly defaultPreference?: ColorModePreference;\n}\n\n/** What {@link useColorModePreference} returns. */\nexport interface ColorModePreferenceState {\n readonly preference: ColorModePreference;\n readonly setPreference: (next: ColorModePreference) => void;\n /** `preference` resolved against the OS — what to put in the theme's `mode` axis. */\n readonly mode: ColorMode;\n}\n\nfunction defaultStorage(): ColorModeStorage | undefined {\n try {\n return globalThis.localStorage ?? undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * A user's color-mode choice, optionally remembered across loads, and the mode\n * it resolves to. Storage that is missing or throws (a private window, blocked\n * site data) degrades to an unremembered choice rather than an error.\n */\nexport function useColorModePreference(\n options: ColorModePreferenceOptions = {},\n): ColorModePreferenceState {\n const { storageKey, storage, defaultPreference = 'auto' } = options;\n\n const [preference, setState] = useState<ColorModePreference>(() => {\n if (storageKey === undefined) return defaultPreference;\n try {\n const stored = (storage ?? defaultStorage())?.getItem(storageKey);\n return isColorModePreference(stored) ? stored : defaultPreference;\n } catch {\n return defaultPreference;\n }\n });\n\n const setPreference = useCallback(\n (next: ColorModePreference) => {\n setState(next);\n if (storageKey === undefined) return;\n try {\n (storage ?? defaultStorage())?.setItem(storageKey, next);\n } catch {\n // Unremembered, but still applied.\n }\n },\n [storageKey, storage],\n );\n\n return { preference, setPreference, mode: useResolvedColorMode(preference) };\n}\n","import type { ColorSource, Palette } from '@weasel-js/paint';\nimport React, { createContext, useCallback, useContext, useMemo, useRef, useLayoutEffect } from 'react';\nimport { applyTheme } from './applyTheme';\nimport { themeTones, type ColorList } from './colorList';\nimport { colorSourceCss } from './colorSource';\nimport { fullSelection, selectionKey, type Selection } from './axes';\nimport { resolveTheme, themeAxes, type ResolvedTheme } from './resolveTheme';\nimport { weaselTheme, type Theme } from './theme';\n\nexport {\n useColorModePreference,\n useResolvedColorMode,\n type ColorModePreferenceOptions,\n type ColorModePreferenceState,\n type ColorModeStorage,\n} from './useColorMode';\nexport { isColorModePreference, type ColorMode, type ColorModePreference } from './colorMode';\n\n/** What `<ThemeProvider>` publishes: the theme, the full selection in force, the\n * fully resolved token record for that pair, and the tone list and palette in force. */\nexport interface ThemeContextValue {\n readonly theme: Theme;\n readonly selection: Selection;\n readonly resolved: ResolvedTheme;\n readonly tones: ColorList;\n readonly palette: Palette;\n}\n\n/** What `<ThemeProvider>` publishes. Provide `null` to hide an outer theme from a subtree, as a\n * boundary that renders its children the way a page with no provider would. */\nexport const ThemeContext = createContext<ThemeContextValue | null>(null);\n\nconst NO_PALETTE: Palette = { entries: [] };\n\n/** Props for `<ThemeProvider>`. */\nexport interface ThemeProviderProps {\n readonly theme?: Theme;\n /** Axis values, e.g. `{ mode: 'light' }`. A missing axis takes its default. */\n readonly selection?: Selection;\n /** The colors a `tone` index picks from in this subtree. Absent: the theme's own. */\n readonly tones?: ColorList;\n /** Colors a `ColorRef` names in this subtree, answered before the theme's own names. Absent: none. */\n readonly palette?: Palette;\n /** Applied to the wrapper element, so it can be the layout element too. */\n readonly className?: string;\n readonly style?: React.CSSProperties;\n readonly children: React.ReactNode;\n}\n\n/**\n * Applies a theme to a wrapper element and publishes the resolved record.\n *\n * Consumers that draw outside the DOM (the WebGL HUD) read `resolved` and\n * never touch `getComputedStyle`.\n *\n * The wrapper is a real element in the layout. Pass `className` rather than\n * nesting your own div inside — an anonymous div between a flex parent and\n * its child breaks percentage heights.\n */\nexport function ThemeProvider({\n theme = weaselTheme,\n selection,\n tones,\n palette = NO_PALETTE,\n className,\n style,\n children,\n}: ThemeProviderProps): React.ReactElement {\n const ref = useRef<HTMLDivElement>(null);\n const axes = themeAxes(theme);\n const full = fullSelection(axes, selection);\n // Keyed on the selection's content: callers pass a fresh object every render.\n const key = selectionKey(axes, full);\n\n const value = useMemo<ThemeContextValue>(\n () => ({ theme, selection: full, resolved: resolveTheme(theme, full), tones: tones ?? themeTones(theme), palette }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [theme, key, tones, palette],\n );\n\n // Applying again replaces what the element held, so only the unmount releases.\n const release = useRef<(() => void) | null>(null);\n useLayoutEffect(() => {\n if (ref.current) release.current = applyTheme(ref.current, theme, value.selection);\n }, [theme, value]);\n useLayoutEffect(() => () => release.current?.(), []);\n\n const attrs = Object.fromEntries(Object.keys(axes).map((a) => [`data-wzl-${a}`, full[a]]));\n return (\n <ThemeContext.Provider value={value}>\n <div ref={ref} className={className} style={style} data-wzl-theme={theme.name} {...attrs}>\n {children}\n </div>\n </ThemeContext.Provider>\n );\n}\n\n/** The theme in force. Throws outside a `<ThemeProvider>`; use\n * `useThemeOptional` where the provider is not guaranteed. */\nexport function useTheme(): ThemeContextValue {\n const ctx = useContext(ThemeContext);\n if (!ctx) throw new Error('useTheme must be used inside a <ThemeProvider>');\n return ctx;\n}\n\n/**\n * The theme if one is provided, `null` otherwise.\n *\n * For kit internals that should follow a theme when the app supplies one but\n * must still work in an app that never mounted a provider — `useHud` is the\n * motivating case.\n */\nexport function useThemeOptional(): ThemeContextValue | null {\n return useContext(ThemeContext);\n}\n\nlet defaultTones: ThemeContextValue | undefined;\n\n/**\n * The tone list in force and what it resolves against — the nearest\n * `<ThemeProvider>`'s, else the built-in theme's at its defaults. Pass the\n * result as `colorAt`'s context.\n */\nexport function useTones(): ThemeContextValue {\n const ctx = useContext(ThemeContext);\n if (ctx) return ctx;\n return (defaultTones ??= {\n theme: weaselTheme,\n selection: fullSelection(themeAxes(weaselTheme)),\n resolved: resolveTheme(weaselTheme),\n tones: themeTones(weaselTheme),\n palette: NO_PALETTE,\n });\n}\n\n/**\n * Turns a `ColorSource` into a CSS color under the theme, tones, and palette in\n * force: `colorSourceCss` against `useTones()`. Every component that takes a\n * color writes it through this. `undefined` for no source, and for one nothing\n * answers.\n */\nexport function useColorCss(): (source: ColorSource | null | undefined) => string | undefined {\n const ctx = useTones();\n return useCallback((source) => (source == null ? undefined : (colorSourceCss(source, ctx) ?? undefined)), [ctx]);\n}\n"]}
1
+ {"version":3,"sources":["../src/useColorMode.ts","../src/react.tsx"],"names":["systemMode","useCallback"],"mappings":"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","file":"react.js","sourcesContent":["import { useCallback, useEffect, useState } from 'react';\nimport { isColorModePreference, type ColorMode, type ColorModePreference } from './colorMode';\n\nfunction systemQuery(): MediaQueryList | null {\n if (typeof globalThis.matchMedia !== 'function') return null;\n return globalThis.matchMedia('(prefers-color-scheme: light)');\n}\n\nconst systemMode = (): ColorMode => (systemQuery()?.matches ? 'light' : 'dark');\n\n/**\n * Resolves a preference to the mode a `ThemeProvider` selection takes.\n * `'auto'` subscribes to the OS setting and follows it live; an explicit\n * choice opts out of the subscription entirely.\n */\nexport function useResolvedColorMode(preference: ColorModePreference): ColorMode {\n const [system, setSystem] = useState<ColorMode>(systemMode);\n\n useEffect(() => {\n if (preference !== 'auto') return;\n const mq = systemQuery();\n if (!mq) return;\n // eslint-disable-next-line react-hooks/set-state-in-effect -- syncs to the OS color-scheme query\n setSystem(mq.matches ? 'light' : 'dark');\n const onChange = (e: { matches: boolean }) => setSystem(e.matches ? 'light' : 'dark');\n mq.addEventListener('change', onChange);\n return () => mq.removeEventListener('change', onChange);\n }, [preference]);\n\n return preference === 'auto' ? system : preference;\n}\n\n/** The part of `Storage` {@link useColorModePreference} reads and writes. */\nexport type ColorModeStorage = Pick<Storage, 'getItem' | 'setItem'>;\n\n/** Options for {@link useColorModePreference}. */\nexport interface ColorModePreferenceOptions {\n /** Where the choice is remembered. Unset, it lasts as long as the component. */\n readonly storageKey?: string;\n /** The store `storageKey` names a slot in. Defaults to `localStorage`. */\n readonly storage?: ColorModeStorage;\n /** The preference before the user has chosen one. Defaults to `'auto'`. */\n readonly defaultPreference?: ColorModePreference;\n}\n\n/** What {@link useColorModePreference} returns. */\nexport interface ColorModePreferenceState {\n readonly preference: ColorModePreference;\n readonly setPreference: (next: ColorModePreference) => void;\n /** `preference` resolved against the OS — what to put in the theme's `mode` axis. */\n readonly mode: ColorMode;\n}\n\nfunction defaultStorage(): ColorModeStorage | undefined {\n try {\n return globalThis.localStorage ?? undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * A user's color-mode choice, optionally remembered across loads, and the mode\n * it resolves to. Storage that is missing or throws (a private window, blocked\n * site data) degrades to an unremembered choice rather than an error.\n */\nexport function useColorModePreference(\n options: ColorModePreferenceOptions = {},\n): ColorModePreferenceState {\n const { storageKey, storage, defaultPreference = 'auto' } = options;\n\n const [preference, setState] = useState<ColorModePreference>(() => {\n if (storageKey === undefined) return defaultPreference;\n try {\n const stored = (storage ?? defaultStorage())?.getItem(storageKey);\n return isColorModePreference(stored) ? stored : defaultPreference;\n } catch {\n return defaultPreference;\n }\n });\n\n const setPreference = useCallback(\n (next: ColorModePreference) => {\n setState(next);\n if (storageKey === undefined) return;\n try {\n (storage ?? defaultStorage())?.setItem(storageKey, next);\n } catch {\n // Unremembered, but still applied.\n }\n },\n [storageKey, storage],\n );\n\n return { preference, setPreference, mode: useResolvedColorMode(preference) };\n}\n","import type { ColorSource, Palette } from '@weasel-js/paint';\nimport React, { createContext, useCallback, useContext, useMemo, useRef, useLayoutEffect } from 'react';\nimport { isDev } from '@weasel-js/registry';\nimport { applyTheme } from './applyTheme';\nimport { themeTones, type ColorList } from './colorList';\nimport { colorSourceCss } from './colorSource';\nimport { fullSelection, selectionKey, type Selection } from './axes';\nimport { resolveTheme, themeAxes, type ResolvedTheme } from './resolveTheme';\nimport { weaselTheme, type Theme } from './theme';\nimport { useResolvedColorMode } from './useColorMode';\n\nexport {\n useColorModePreference,\n useResolvedColorMode,\n type ColorModePreferenceOptions,\n type ColorModePreferenceState,\n type ColorModeStorage,\n} from './useColorMode';\nexport { isColorModePreference, type ColorMode, type ColorModePreference } from './colorMode';\n\n/** What `<ThemeProvider>` publishes: the theme, the full selection in force, the\n * fully resolved token record for that pair, and the tone list and palette in force. */\nexport interface ThemeContextValue {\n readonly theme: Theme;\n readonly selection: Selection;\n readonly resolved: ResolvedTheme;\n readonly tones: ColorList;\n readonly palette: Palette;\n}\n\n/** What `<ThemeProvider>` publishes. Provide `null` to hide an outer theme from a subtree, as a\n * boundary that renders its children the way a page with no provider would. */\nexport const ThemeContext = createContext<ThemeContextValue | null>(null);\n\nconst NO_PALETTE: Palette = { entries: [] };\n\n/** Props for `<ThemeProvider>`. */\nexport interface ThemeProviderProps {\n readonly theme?: Theme;\n /** Axis values, e.g. `{ mode: 'light' }`. A missing axis takes its default. `mode: 'auto'`\n * follows the OS setting, live; any other value the theme lacks takes the default, with a\n * warning in development. */\n readonly selection?: Selection;\n /** The colors a `tone` index picks from in this subtree. Absent: the theme's own. */\n readonly tones?: ColorList;\n /** Colors a `ColorRef` names in this subtree, answered before the theme's own names. Absent: none. */\n readonly palette?: Palette;\n /** Applied to the wrapper element, so it can be the layout element too. */\n readonly className?: string;\n readonly style?: React.CSSProperties;\n readonly children: React.ReactNode;\n}\n\n/**\n * Applies a theme to a wrapper element and publishes the resolved record.\n *\n * Consumers that draw outside the DOM (the WebGL HUD) read `resolved` and\n * never touch `getComputedStyle`.\n *\n * The wrapper is a real element in the layout. Pass `className` rather than\n * nesting your own div inside — an anonymous div between a flex parent and\n * its child breaks percentage heights.\n */\nexport function ThemeProvider({\n theme = weaselTheme,\n selection,\n tones,\n palette = NO_PALETTE,\n className,\n style,\n children,\n}: ThemeProviderProps): React.ReactElement {\n const ref = useRef<HTMLDivElement>(null);\n const axes = themeAxes(theme);\n const autoMode = selection?.mode === 'auto' && !('auto' in (axes.mode?.values ?? {}));\n const systemMode = useResolvedColorMode(autoMode ? 'auto' : 'dark');\n const given = autoMode ? { ...selection, mode: systemMode } : selection;\n const full = fullSelection(axes, given);\n warnUnknownAxisValues(theme, axes, given);\n // Keyed on the selection's content: callers pass a fresh object every render.\n const key = selectionKey(axes, full);\n\n const value = useMemo<ThemeContextValue>(\n () => ({ theme, selection: full, resolved: resolveTheme(theme, full), tones: tones ?? themeTones(theme), palette }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [theme, key, tones, palette],\n );\n\n // Applying again replaces what the element held, so only the unmount releases.\n const release = useRef<(() => void) | null>(null);\n useLayoutEffect(() => {\n if (ref.current) release.current = applyTheme(ref.current, theme, value.selection);\n }, [theme, value]);\n useLayoutEffect(() => () => release.current?.(), []);\n\n const attrs = Object.fromEntries(Object.keys(axes).map((a) => [`data-wzl-${a}`, full[a]]));\n return (\n <ThemeContext.Provider value={value}>\n <div ref={ref} className={className} style={style} data-wzl-theme={theme.name} {...attrs}>\n {children}\n </div>\n </ThemeContext.Provider>\n );\n}\n\nconst warned = new Set<string>();\n\nfunction warnUnknownAxisValues(theme: Theme, axes: ReturnType<typeof themeAxes>, selection: Selection | undefined): void {\n if (!selection || !isDev()) return;\n for (const [axis, value] of Object.entries(selection)) {\n const def = axes[axis];\n if (!def || value in def.values) continue;\n const key = `${theme.name}\\0${axis}\\0${value}`;\n if (warned.has(key)) continue;\n warned.add(key);\n console.warn(\n `[weasel theme] ThemeProvider: theme \"${theme.name}\" has no ${axis} \"${value}\"; using \"${def.default}\". Its ${axis} values: ${Object.keys(def.values).join(', ')}.`,\n );\n }\n}\n\n/** The theme in force. Throws outside a `<ThemeProvider>`; use\n * `useThemeOptional` where the provider is not guaranteed. */\nexport function useTheme(): ThemeContextValue {\n const ctx = useContext(ThemeContext);\n if (!ctx) throw new Error('useTheme must be used inside a <ThemeProvider>');\n return ctx;\n}\n\n/**\n * The theme if one is provided, `null` otherwise.\n *\n * For kit internals that should follow a theme when the app supplies one but\n * must still work in an app that never mounted a provider — `useHud` is the\n * motivating case.\n */\nexport function useThemeOptional(): ThemeContextValue | null {\n return useContext(ThemeContext);\n}\n\nlet defaultTones: ThemeContextValue | undefined;\n\n/**\n * The tone list in force and what it resolves against — the nearest\n * `<ThemeProvider>`'s, else the built-in theme's at its defaults. Pass the\n * result as `colorAt`'s context.\n */\nexport function useTones(): ThemeContextValue {\n const ctx = useContext(ThemeContext);\n if (ctx) return ctx;\n return (defaultTones ??= {\n theme: weaselTheme,\n selection: fullSelection(themeAxes(weaselTheme)),\n resolved: resolveTheme(weaselTheme),\n tones: themeTones(weaselTheme),\n palette: NO_PALETTE,\n });\n}\n\n/**\n * Turns a `ColorSource` into a CSS color under the theme, tones, and palette in\n * force: `colorSourceCss` against `useTones()`. Every component that takes a\n * color writes it through this. `undefined` for no source, and for one nothing\n * answers.\n */\nexport function useColorCss(): (source: ColorSource | null | undefined) => string | undefined {\n const ctx = useTones();\n return useCallback((source) => (source == null ? undefined : (colorSourceCss(source, ctx) ?? undefined)), [ctx]);\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weasel-js/theme",
3
- "version": "1.10.1",
3
+ "version": "1.11.1",
4
4
  "description": "Design tokens shared by weasel-ui and weasel-hud — CSS variables + a parallel TS export.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -43,9 +43,9 @@
43
43
  "./package.json": "./package.json"
44
44
  },
45
45
  "dependencies": {
46
- "@weasel-js/paint": "1.10.1",
47
- "@weasel-js/prefs": "1.10.1",
48
- "@weasel-js/registry": "1.10.1"
46
+ "@weasel-js/paint": "1.11.1",
47
+ "@weasel-js/prefs": "1.11.1",
48
+ "@weasel-js/registry": "1.11.1"
49
49
  },
50
50
  "peerDependencies": {
51
51
  "react": ">=18"