@weasel-js/theme 1.5.1 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/faces.css ADDED
@@ -0,0 +1,19 @@
1
+ /* The kit's two faces, declared and nothing else. `fonts.css` pairs them with a
2
+ * document font; `@weasel-js/labkit` carries them in its stylesheet.
3
+ * Licenses: OFL-Oswald.txt and OFL-Inter.txt in @weasel-js/theme's fonts/. */
4
+
5
+ @font-face {
6
+ font-family: 'Oswald';
7
+ font-style: normal;
8
+ font-weight: 200 700;
9
+ font-display: swap;
10
+ src: url('../fonts/oswald-latin-variable.woff2') format('woff2');
11
+ }
12
+
13
+ @font-face {
14
+ font-family: 'Inter';
15
+ font-style: normal;
16
+ font-weight: 400;
17
+ font-display: swap;
18
+ src: url('../fonts/inter-latin.woff2') format('woff2');
19
+ }
package/dist/fonts.css CHANGED
@@ -1,26 +1,12 @@
1
1
  /* Opt-in typography: the kit's intended faces, and the document set in them.
2
2
  * Import this only if you want both — the token font stacks fall back to
3
3
  * system-ui without the faces, and `tokens.css` sets no font of its own, so an
4
- * app with its own typography can import the tokens and skip this file.
5
- * Licenses: ../fonts/OFL-Oswald.txt, ../fonts/OFL-Inter.txt */
4
+ * app with its own typography can import the tokens and skip this file, or
5
+ * take `faces.css` for the faces alone. */
6
+
7
+ @import './faces.css';
6
8
 
7
9
  :root {
8
10
  font-family: var(--wzl-font-ui);
9
11
  font-weight: var(--wzl-font-weight-normal);
10
12
  }
11
-
12
- @font-face {
13
- font-family: 'Oswald';
14
- font-style: normal;
15
- font-weight: 200 700;
16
- font-display: swap;
17
- src: url('../fonts/oswald-latin-variable.woff2') format('woff2');
18
- }
19
-
20
- @font-face {
21
- font-family: 'Inter';
22
- font-style: normal;
23
- font-weight: 400;
24
- font-display: swap;
25
- src: url('../fonts/inter-latin.woff2') format('woff2');
26
- }
package/dist/index.d.ts CHANGED
@@ -1,7 +1,6 @@
1
- import { T as TokenName, R as ResolvedTheme } from './resolveTheme-CHnPdh8W.js';
2
- export { B as BAKED_THEMES, G as GeneratedTheme, a as THEMES, b as THEME_SOURCES, r as resolveTheme, t as themeAxes } from './resolveTheme-CHnPdh8W.js';
3
- import { T as Theme, S as Selection, a as TokenValue } from './theme-BtpBHcUg.js';
4
- export { A as AxisDef, b as AxisDefs, c as AxisValue, B as BakedTheme, d as ByAxis, F as FlatTokens, P as PinObject, e as PinValue, R as RawToken, f as ThemeDefinition, g as ThemeInput, V as Varying, h as defineTheme, i as enumerateSelections, j as fullSelection, k as isByAxis, s as selectionKey, w as weaselTheme } from './theme-BtpBHcUg.js';
1
+ import { T as TokenName, R as ResolvedTheme, a as Theme, S as Selection, b as TokenValue } from './themes-bTfTZjCh.js';
2
+ export { A as AxisDef, c as AxisDefs, d as AxisValue, B as BAKED_THEMES, e as BakedTheme, f as ByAxis, C as ColorContext, g as ColorList, F as FlatTokens, G as GeneratedColors, h as GeneratedTheme, P as PinObject, i as PinValue, j as RampColors, k as RawToken, l as SerializableColorList, m as THEMES, n as THEME_SOURCES, o as ThemeDefinition, p as ThemeInput, V as Varying, q as colorAt, r as colorCount, s as colorCssAt, t as defineTheme, u as enumerateSelections, v as fullSelection, w as isByAxis, x as rampSteps, y as resolveTheme, z as selectionKey, D as themeAxes, E as themeTones, H as weaselTheme } from './themes-bTfTZjCh.js';
3
+ export { C as ColorMode, a as ColorModePreference, i as isColorModePreference } from './colorMode-CoklZomJ.js';
5
4
 
6
5
  /** One row of `TOKEN_MANIFEST`: a token, its DTCG type, the name-prefix
7
6
  * group it belongs to, its value in the default theme and selection, and
@@ -52,11 +51,14 @@ interface DtcgDocument {
52
51
  extends?: Theme | null;
53
52
  primitives?: Record<string, unknown>;
54
53
  modes?: Record<string, Record<string, unknown>>;
54
+ $extensions?: Record<string, unknown>;
55
55
  }
56
56
  /**
57
57
  * Build a `Theme` from a DTCG document — the interchange path, for tokens
58
58
  * exported by a design tool rather than authored in TS. A token a mode leaves
59
59
  * out takes the document's primitive of the same name, or else the base theme's.
60
+ * Axes besides mode come from `$extensions["com.weasel.axes"]`, the encoding
61
+ * `toDTCG` writes; a document without it loads with mode as its only axis.
60
62
  */
61
63
  declare function loadDTCG(doc: DtcgDocument): Theme;
62
64
 
@@ -73,4 +75,46 @@ declare function serializeTokenValue(type: string, value: TokenValue): string;
73
75
  */
74
76
  declare function parseTokenValue(type: string, text: string): TokenValue;
75
77
 
76
- export { ResolvedTheme, Selection, TOKEN_MANIFEST, Theme, type TokenManifestEntry, TokenName, TokenValue, applyTheme, loadDTCG, parseTokenValue, serializeTokenValue, tokenPx };
78
+ /**
79
+ * The vocabulary of stances, and the slots a theme styles a stanced surface with.
80
+ *
81
+ * A stance looks the same on every surface that takes one. A surface reads
82
+ * `--wzl-stance-<stance>-<slot>` first — a nested panel
83
+ * `--wzl-stance-<stance>-nested-<slot>` before that — and falls back to its
84
+ * own base look, so a theme declares only the slots where a stance differs and
85
+ * every other name here is read, with its fallback, and declared by nothing.
86
+ * A panel's base look is itself theme slots, `--wzl-panel-<slot>`.
87
+ */
88
+ /** What kind of content a surface holds — a class of content, never a position. */
89
+ declare const STANCES: readonly ["scope", "aside", "advanced", "debug", "danger", "notice", "important", "preview"];
90
+ type Stance = (typeof STANCES)[number];
91
+ /** One styleable property of a stanced surface. */
92
+ interface StanceSlot {
93
+ readonly name: string;
94
+ /** What it drives, for tooling. */
95
+ readonly drives: string;
96
+ }
97
+ declare const STANCE_SLOTS: readonly StanceSlot[];
98
+ /** Every stance-slot name, `--wzl-`-prefixed, nested variants included. */
99
+ declare function stanceSlotNames(): string[];
100
+ /** What `resolveStanceSlots` resolves a surface against, besides its tokens. */
101
+ interface StanceLookup<S extends string> {
102
+ readonly stance?: Stance;
103
+ /** The surface sits inside one of its own kind, and reads `-nested-<slot>` first. */
104
+ readonly nested?: boolean;
105
+ /** A stanced surface's fallback, where it differs from `base`. */
106
+ readonly stanced?: Partial<Readonly<Record<S, string>>>;
107
+ }
108
+ /**
109
+ * A stanced surface's look, for one drawn without the cascade — a canvas
110
+ * surface reading a resolved theme. The same lookup the generated CSS makes:
111
+ * `--wzl-stance-<stance>-<slot>`, then the stanced fallback, then `base`.
112
+ * `base` holds final values, one per slot the surface draws.
113
+ *
114
+ * The tone is the caller's: a surface that names one mixes it into
115
+ * `surface` at `tone-mix`; one that names none takes the `tone` slot, which
116
+ * falls back to `surface` so mixing it changes nothing.
117
+ */
118
+ declare function resolveStanceSlots<S extends string>(tokens: Readonly<Record<string, string>>, base: Readonly<Record<S, string>>, { stance, nested, stanced }?: StanceLookup<S>): Record<S, string>;
119
+
120
+ export { ResolvedTheme, STANCES, STANCE_SLOTS, Selection, type Stance, type StanceLookup, type StanceSlot, TOKEN_MANIFEST, Theme, type TokenManifestEntry, TokenName, TokenValue, applyTheme, loadDTCG, parseTokenValue, resolveStanceSlots, serializeTokenValue, stanceSlotNames, tokenPx };
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
- import { weaselTheme } from './chunk-YBHW5LZM.js';
2
- export { BAKED_THEMES, THEMES, THEME_SOURCES, applyTheme, defineTheme, weaselTheme } from './chunk-YBHW5LZM.js';
3
- import { ALPHA_EXT } from './chunk-PJMTWM34.js';
4
- import { themeAxes } from './chunk-LL2ERJPZ.js';
5
- export { enumerateSelections, fullSelection, isByAxis, parseTokenValue, resolveTheme, selectionKey, serializeTokenValue, themeAxes } from './chunk-LL2ERJPZ.js';
1
+ import { weaselTheme } from './chunk-6R3FTRRJ.js';
2
+ export { BAKED_THEMES, THEMES, THEME_SOURCES, applyTheme, colorAt, colorCount, colorCssAt, defineTheme, isColorModePreference, rampSteps, themeTones, weaselTheme } from './chunk-6R3FTRRJ.js';
3
+ import { AXES_EXT, overrideKey, ALPHA_EXT } from './chunk-GI7ZAXIB.js';
4
+ import { themeAxes, mergeAxes } from './chunk-QKRFHBRB.js';
5
+ export { enumerateSelections, fullSelection, isByAxis, parseTokenValue, resolveTheme, selectionKey, serializeTokenValue, themeAxes } from './chunk-QKRFHBRB.js';
6
6
 
7
7
  // src/generated/manifest.ts
8
8
  var TOKEN_MANIFEST = [
@@ -19,6 +19,9 @@ var TOKEN_MANIFEST = [
19
19
  { name: "--wzl-accent-soft", type: "color", group: "accent", defaultValue: "#1d1454", description: "", hook: false },
20
20
  { name: "--wzl-accent-base", type: "color", group: "accent", defaultValue: "#2e1f7a", description: "Midnight blue-violet. Deep and saturated; anchors primary actions on light and dark surfaces alike.", hook: false },
21
21
  { name: "--wzl-accent-strong", type: "color", group: "accent", defaultValue: "#5841b8", description: "", hook: false },
22
+ { name: "--wzl-secondary-soft", type: "color", group: "secondary", defaultValue: "#704e09", description: "", hook: false },
23
+ { name: "--wzl-secondary-base", type: "color", group: "secondary", defaultValue: "#dc9d15", description: "", hook: false },
24
+ { name: "--wzl-secondary-strong", type: "color", group: "secondary", defaultValue: "#f1cb8d", description: "", hook: false },
22
25
  { name: "--wzl-swatch-fuchsia", type: "color", group: "swatch", defaultValue: "#f641f7", description: "First of ten categorical spot colors for plotting, status and data viz. Take them in sequence: the order is farthest-point over hue, so the first N are as separated as N colors can be (first three 87\xB0 apart, all ten 32\xB0). Mode-invariant on purpose \u2014 a series keeps its color when the mode flips, which is why they are tuned for a dark surface and clear only 1.5:1 on a light one.", hook: false },
23
26
  { name: "--wzl-swatch-green", type: "color", group: "swatch", defaultValue: "#48e628", description: "", hook: false },
24
27
  { name: "--wzl-swatch-sky", type: "color", group: "swatch", defaultValue: "#36bff2", description: "", hook: false },
@@ -29,6 +32,20 @@ var TOKEN_MANIFEST = [
29
32
  { name: "--wzl-swatch-citron", type: "color", group: "swatch", defaultValue: "#d5d23a", description: "", hook: false },
30
33
  { name: "--wzl-swatch-rose", type: "color", group: "swatch", defaultValue: "#fa4590", description: "", hook: false },
31
34
  { name: "--wzl-swatch-violet", type: "color", group: "swatch", defaultValue: "#955cf9", description: "", hook: false },
35
+ { name: "--wzl-font-size-2xs", type: "dimension", group: "font", defaultValue: "9px", description: "Shortcut keys and index badges. The floor for chrome text.", hook: false },
36
+ { name: "--wzl-font-size-xs", type: "dimension", group: "font", defaultValue: "10px", description: "Tool-button labels under an icon.", hook: false },
37
+ { name: "--wzl-font-size-sm", type: "dimension", group: "font", defaultValue: "11px", description: "Chrome labels, status readouts, section headings.", hook: false },
38
+ { name: "--wzl-font-size-md", type: "dimension", group: "font", defaultValue: "13px", description: "Controls and body text.", hook: false },
39
+ { name: "--wzl-font-size-lg", type: "dimension", group: "font", defaultValue: "16px", description: "Panel and dialog titles.", hook: false },
40
+ { name: "--wzl-font-size-xl", type: "dimension", group: "font", defaultValue: "20px", description: "The largest chrome text \u2014 a lab or app title.", hook: false },
41
+ { name: "--wzl-space-1", type: "dimension", group: "space", defaultValue: "2px", description: "", hook: false },
42
+ { name: "--wzl-space-2", type: "dimension", group: "space", defaultValue: "4px", description: "", hook: false },
43
+ { name: "--wzl-space-3", type: "dimension", group: "space", defaultValue: "6px", description: "", hook: false },
44
+ { name: "--wzl-space-4", type: "dimension", group: "space", defaultValue: "8px", description: "", hook: false },
45
+ { name: "--wzl-space-5", type: "dimension", group: "space", defaultValue: "10px", description: "", hook: false },
46
+ { name: "--wzl-space-6", type: "dimension", group: "space", defaultValue: "12px", description: "", hook: false },
47
+ { name: "--wzl-space-7", type: "dimension", group: "space", defaultValue: "14px", description: "", hook: false },
48
+ { name: "--wzl-space-8", type: "dimension", group: "space", defaultValue: "16px", description: "", hook: false },
32
49
  { name: "--wzl-surface", type: "color", group: "surface", defaultValue: "#181a1e", description: "", hook: false },
33
50
  { name: "--wzl-surface-raised", type: "color", group: "surface", defaultValue: "#25272c", description: "", hook: false },
34
51
  { name: "--wzl-surface-sunken", type: "color", group: "surface", defaultValue: "#0e0f12", description: "", hook: false },
@@ -38,11 +55,51 @@ var TOKEN_MANIFEST = [
38
55
  { name: "--wzl-border", type: "color", group: "border", defaultValue: "#25272c", description: "", hook: false },
39
56
  { name: "--wzl-border-strong", type: "color", group: "border", defaultValue: "#6f737b", description: "The line that has to be seen: a control's edge, a raised surface's outline. Clears WCAG 1.4.11's 3:1 against every surface in both modes.", hook: false },
40
57
  { name: "--wzl-accent-fg", type: "color", group: "accent", defaultValue: "#5841b8", description: "Accent for foreground use \u2014 text, icons, selected labels drawn on a surface. accent is a fill; the midnight base is too dim to read as text on dark surfaces.", hook: false },
58
+ { name: "--wzl-secondary-fg", type: "color", group: "secondary", defaultValue: "#dc9d15", description: "Secondary accent for foreground use: a second voice beside the accent, such as a value the panel derived rather than one the user set. Picked from the ramp so it clears 4.5:1 on the surface in both modes.", hook: false },
41
59
  { name: "--wzl-fg-inverse", type: "color", group: "fg", defaultValue: "#0e0f12", description: "Text drawn on a filled foreground-colored element (e.g. a slider thumb). Flips with the mode, so it is a semantic, not an alias to a primitive.", hook: false },
42
60
  { name: "--wzl-shadow", type: "color", group: "shadow", defaultValue: "rgba(0, 0, 0, 0.6)", description: "Elevation. Always darker than any surface \u2014 never derived from fg, which is near-white on dark.", hook: false },
43
- { name: "--wzl-danger-base", type: "color", group: "danger", defaultValue: "#d94a3f", description: "", hook: false },
44
- { name: "--wzl-warning-base", type: "color", group: "warning", defaultValue: "#d99a3f", description: "", hook: false },
45
- { name: "--wzl-success-base", type: "color", group: "success", defaultValue: "#2ec27e", description: "", hook: false },
61
+ { name: "--wzl-panel-surface", type: "color", group: "panel", defaultValue: "#25272c", description: "Fill of a PropertyPanel.", hook: false },
62
+ { name: "--wzl-panel-border-color", type: "color", group: "panel", defaultValue: "#25272c", description: "", hook: false },
63
+ { name: "--wzl-panel-border-style", type: "string", group: "panel", defaultValue: "solid", description: "", hook: false },
64
+ { name: "--wzl-panel-border-width", type: "dimension", group: "panel", defaultValue: "1px", description: "", hook: false },
65
+ { name: "--wzl-panel-radius", type: "dimension", group: "panel", defaultValue: "14px", description: "", hook: false },
66
+ { name: "--wzl-panel-pad", type: "dimension", group: "panel", defaultValue: "14px 16px", description: "A panel\u2019s padding when no density sets one.", hook: false },
67
+ { name: "--wzl-panel-blur", type: "dimension", group: "panel", defaultValue: "3px", description: "", hook: false },
68
+ { name: "--wzl-panel-tone-mix", type: "number", group: "panel", defaultValue: "14%", description: "How much of a panel\u2019s tone mixes into its surface.", hook: false },
69
+ { name: "--wzl-panel-title-font", type: "fontFamily", group: "panel", defaultValue: "Oswald, 'Helvetica Neue Condensed', 'Arial Narrow', system-ui, sans-serif", description: "", hook: false },
70
+ { name: "--wzl-panel-title-weight", type: "fontWeight", group: "panel", defaultValue: "200", description: "", hook: false },
71
+ { name: "--wzl-panel-title-size", type: "dimension", group: "panel", defaultValue: "16px", description: "", hook: false },
72
+ { name: "--wzl-panel-title-case", type: "string", group: "panel", defaultValue: "none", description: "", hook: false },
73
+ { name: "--wzl-panel-title-tracking", type: "dimension", group: "panel", defaultValue: "0.01em", description: "", hook: false },
74
+ { name: "--wzl-panel-title-color", type: "color", group: "panel", defaultValue: "#e6e7e9", description: "", hook: false },
75
+ { name: "--wzl-panel-title-inset", type: "dimension", group: "panel", defaultValue: "0", description: "", hook: false },
76
+ { name: "--wzl-stance-scope-border-width", type: "dimension", group: "stance", defaultValue: "0", description: "", hook: false },
77
+ { name: "--wzl-stance-scope-radius", type: "dimension", group: "stance", defaultValue: "8px", description: "", hook: false },
78
+ { name: "--wzl-stance-scope-tone-mix", type: "number", group: "stance", defaultValue: "22%", description: "", hook: false },
79
+ { name: "--wzl-stance-scope-nested-radius", type: "dimension", group: "stance", defaultValue: "6px", description: "", hook: false },
80
+ { name: "--wzl-stance-scope-nested-surface", type: "color", group: "stance", defaultValue: "#181a1e", description: "A scope panel inside another sits back into it.", hook: false },
81
+ { name: "--wzl-stance-aside-surface", type: "color", group: "stance", defaultValue: "transparent", description: "", hook: false },
82
+ { name: "--wzl-stance-aside-border-color", type: "color", group: "stance", defaultValue: "rgba(230, 231, 233, 0.1)", description: "", hook: false },
83
+ { name: "--wzl-stance-aside-blur", type: "dimension", group: "stance", defaultValue: "0px", description: "", hook: false },
84
+ { name: "--wzl-stance-aside-title-color", type: "color", group: "stance", defaultValue: "#6f737b", description: "", hook: false },
85
+ { name: "--wzl-stance-advanced-title-color", type: "color", group: "stance", defaultValue: "#6f737b", description: "", hook: false },
86
+ { name: "--wzl-stance-debug-border-style", type: "string", group: "stance", defaultValue: "dashed", description: "", hook: false },
87
+ { name: "--wzl-stance-debug-title-font", type: "fontFamily", group: "stance", defaultValue: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace", description: "", hook: false },
88
+ { name: "--wzl-stance-debug-title-case", type: "string", group: "stance", defaultValue: "none", description: "", hook: false },
89
+ { name: "--wzl-stance-danger-tone", type: "color", group: "stance", defaultValue: "#d94a3f", description: "", hook: false },
90
+ { name: "--wzl-stance-danger-border-color", type: "color", group: "stance", defaultValue: "#d94a3f", description: "", hook: false },
91
+ { name: "--wzl-stance-danger-title-color", type: "color", group: "stance", defaultValue: "#d94a3f", description: "", hook: false },
92
+ { name: "--wzl-stance-notice-tone", type: "color", group: "stance", defaultValue: "#2e1f7a", description: "", hook: false },
93
+ { name: "--wzl-stance-notice-tone-mix", type: "number", group: "stance", defaultValue: "8%", description: "", hook: false },
94
+ { name: "--wzl-stance-important-border-color", type: "color", group: "stance", defaultValue: "#5841b8", description: "", hook: false },
95
+ { name: "--wzl-stance-important-title-color", type: "color", group: "stance", defaultValue: "#5841b8", description: "", hook: false },
96
+ { name: "--wzl-stance-preview-pad", type: "dimension", group: "stance", defaultValue: "0", description: "", hook: false },
97
+ { name: "--wzl-stance-preview-title-size", type: "dimension", group: "stance", defaultValue: "10px", description: "", hook: false },
98
+ { name: "--wzl-stance-preview-title-case", type: "string", group: "stance", defaultValue: "none", description: "", hook: false },
99
+ { name: "--wzl-stance-preview-title-inset", type: "dimension", group: "stance", defaultValue: "6px 10px 0", description: "", hook: false },
100
+ { name: "--wzl-danger-base", type: "color", group: "status", defaultValue: "#d94a3f", description: "", hook: false },
101
+ { name: "--wzl-warning-base", type: "color", group: "status", defaultValue: "#d99a3f", description: "", hook: false },
102
+ { name: "--wzl-success-base", type: "color", group: "status", defaultValue: "#2ec27e", description: "", hook: false },
46
103
  { name: "--wzl-fg-on-accent", type: "color", group: "fg", defaultValue: "#f5f5f6", description: "", hook: false },
47
104
  { name: "--wzl-line-subtle", type: "color", group: "line", defaultValue: "rgba(230, 231, 233, 0.1)", description: "Barely-there separators (table rows).", hook: false },
48
105
  { name: "--wzl-line", type: "color", group: "line", defaultValue: "rgba(230, 231, 233, 0.2)", description: "Primary gridlines on sunken surfaces.", hook: false },
@@ -50,9 +107,10 @@ var TOKEN_MANIFEST = [
50
107
  { name: "--wzl-curve-color", type: "color", group: "curve", defaultValue: "#5841b8", description: "Drawn data curves. Routes through the bright accent because accent-base is too dim on sunken surfaces.", hook: false },
51
108
  { name: "--wzl-accent", type: "color", group: "accent", defaultValue: "#2e1f7a", description: "", hook: false },
52
109
  { name: "--wzl-accent-hover", type: "color", group: "accent", defaultValue: "#5841b8", description: "", hook: false },
53
- { name: "--wzl-danger", type: "color", group: "danger", defaultValue: "#d94a3f", description: "", hook: false },
54
- { name: "--wzl-warning", type: "color", group: "warning", defaultValue: "#d99a3f", description: "", hook: false },
55
- { name: "--wzl-success", type: "color", group: "success", defaultValue: "#2ec27e", description: "", hook: false },
110
+ { name: "--wzl-secondary", type: "color", group: "secondary", defaultValue: "#dc9d15", description: "Honey amber in dark mode and teal in light, where a darkened amber reads as brown. A fill; use secondary-fg for text.", hook: false },
111
+ { name: "--wzl-danger", type: "color", group: "status", defaultValue: "#d94a3f", description: "", hook: false },
112
+ { name: "--wzl-warning", type: "color", group: "status", defaultValue: "#d99a3f", description: "", hook: false },
113
+ { name: "--wzl-success", type: "color", group: "status", defaultValue: "#2ec27e", description: "", hook: false },
56
114
  { name: "--wzl-focus-ring", type: "color", group: "focus", defaultValue: "#5841b8", description: "", hook: false },
57
115
  { name: "--wzl-glass-tint", type: "color", group: "glass", defaultValue: "#2e1f7a", description: "Frosted-glass tint. Components compose it as color-mix(in srgb, var(--wzl-glass-tint) 78%, transparent) with backdrop-filter: blur(3px).", hook: false },
58
116
  { name: "--wzl-surface-hover", type: "color", group: "surface", defaultValue: "rgba(230, 231, 233, 0.1)", description: "Hover wash over a raised surface. Same formula as line-subtle, different job \u2014 do not collapse them.", hook: false },
@@ -64,8 +122,11 @@ var TOKEN_MANIFEST = [
64
122
  { name: "--wzl-border-w", type: "dimension", group: "border", defaultValue: "1px", description: "", hook: false },
65
123
  { name: "--wzl-line-width", type: "dimension", group: "line", defaultValue: "2px", description: "Structural lines \u2014 gridlines, dividers, axes.", hook: false },
66
124
  { name: "--wzl-curve-width", type: "dimension", group: "curve", defaultValue: "3px", description: "Drawn data curves. Heavier than structural lines so data reads as primary content.", hook: false },
67
- { name: "--wzl-tb-height", type: "dimension", group: "tb", defaultValue: "28px", description: "Height of a horizontal toolbar strip (ActionsBar, OptionsBar, ToolOptionsBar). Taller than control-h so the controls inside a strip are not flush with its edges.", hook: false },
125
+ { name: "--wzl-tb-height", type: "dimension", group: "tb", defaultValue: "28px", description: "Height of a horizontal toolbar strip (ButtonBar, OptionsBar, ToolOptionsBar). Taller than control-h so the controls inside a strip are not flush with its edges.", hook: false },
126
+ { name: "--wzl-control-h-xs", type: "dimension", group: "control", defaultValue: "18px", description: "Height of the smallest interactive control -- a stepper, a keycap, a swatch button in a dense row. The rank below control-h-sm.", hook: false },
127
+ { name: "--wzl-control-h-sm", type: "dimension", group: "control", defaultValue: "20px", description: "Height of a control in a dense inspector -- a Properties field, a small Button. The rank below control-h, which sizes a standalone control.", hook: false },
68
128
  { name: "--wzl-control-h", type: "dimension", group: "control", defaultValue: "24px", description: "Height of an interactive control \u2014 button, input, select, segmented control. Distinct from tb-height, which sizes the strip a row of them sits in.", hook: false },
129
+ { name: "--wzl-icon-button-size", type: "dimension", group: "icon", defaultValue: "22px", description: "Side of a square icon button -- a dialog close, a row remove, a field clear. Smaller than control-h because it carries a glyph rather than a label, and square, so it sizes both axes.", hook: false },
69
130
  { name: "--wzl-slider-track-h", type: "dimension", group: "slider", defaultValue: "4px", description: "Height of a linear range track. The kit's single-thumb ranges all wear this; Slider's default 24px canvas track does not.", hook: false },
70
131
  { name: "--wzl-slider-thumb-size", type: "dimension", group: "slider", defaultValue: "8px", description: "Diameter of a linear range thumb.", hook: false },
71
132
  { name: "--wzl-slider-track-mix", type: "dimension", group: "slider", defaultValue: "18%", description: "Accent proportion in a range track, as a color-mix percentage \u2014 not a color. Mixed into a real property, never into another custom property.", hook: false },
@@ -73,18 +134,14 @@ var TOKEN_MANIFEST = [
73
134
  { name: "--wzl-handle-size", type: "dimension", group: "handle", defaultValue: "9px", description: "Edge of a draggable handle on a plot or timeline \u2014 a keyframe diamond, a timeline key, an event mark. The default rank; -sm and -lg are the two deliberate departures from it.", hook: false },
74
135
  { name: "--wzl-handle-size-sm", type: "dimension", group: "handle", defaultValue: "7px", description: "A handle that is drawn but not draggable \u2014 a locked or pinned point. Smaller so it reads as inert next to a live one.", hook: false },
75
136
  { name: "--wzl-handle-size-lg", type: "dimension", group: "handle", defaultValue: "10px", description: "A handle that is a category of its own rather than one of a row \u2014 a curve's endpoint. Larger so it separates from the handles it sits in line with.", hook: false },
137
+ { name: "--wzl-checker-a", type: "color", group: "checker", defaultValue: "#25272c", description: "One square of the transparency checker drawn under a paint that is empty, mixed or translucent. Paint it as repeating-conic-gradient(var(--wzl-checker-a) 0% 25%, var(--wzl-checker-b) 0% 50%) 50% / var(--wzl-checker-size) var(--wzl-checker-size).", hook: false },
138
+ { name: "--wzl-checker-b", type: "color", group: "checker", defaultValue: "#181a1e", description: "The other square of the transparency checker.", hook: false },
139
+ { name: "--wzl-checker-size", type: "dimension", group: "checker", defaultValue: "8px", description: "Edge of one repeat of the transparency checker: two squares by two.", hook: false },
140
+ { name: "--wzl-workspace-surface", type: "color", group: "workspace", defaultValue: "#0e0f12", description: "Ground of a canvas app's workspace \u2014 the area around the document page.", hook: false },
141
+ { name: "--wzl-workspace-line", type: "color", group: "workspace", defaultValue: "rgba(230, 231, 233, 0.1)", description: "Marks drawn over workspace-surface, such as its diagonal stripes, so the page reads as the thing on top.", hook: false },
76
142
  { name: "--wzl-field-pad-x", type: "dimension", group: "field", defaultValue: "8px", description: "Horizontal padding inside a boxed field. Pairs with --wzl-field-h, which is a container override and has no :root default.", hook: false },
77
143
  { name: "--wzl-glass-blur", type: "dimension", group: "glass", defaultValue: "3px", description: "Backdrop blur radius for frosted surfaces. Pairs with glass-tint.", hook: false },
78
- { name: "--wzl-font-size-2xs", type: "dimension", group: "font", defaultValue: "9px", description: "Shortcut keys and index badges. The floor for chrome text.", hook: false },
79
- { name: "--wzl-font-size-xs", type: "dimension", group: "font", defaultValue: "10px", description: "Tool-button labels under an icon.", hook: false },
80
- { name: "--wzl-font-size-sm", type: "dimension", group: "font", defaultValue: "11px", description: "Chrome labels, status readouts, section headings.", hook: false },
81
144
  { name: "--wzl-font-size", type: "dimension", group: "font", defaultValue: "13px", description: "Controls and body text.", hook: false },
82
- { name: "--wzl-font-size-lg", type: "dimension", group: "font", defaultValue: "16px", description: "Panel and dialog titles.", hook: false },
83
- { name: "--wzl-font-size-xl", type: "dimension", group: "font", defaultValue: "20px", description: "The largest chrome text \u2014 a lab or app title.", hook: false },
84
- { name: "--wzl-space-xs", type: "dimension", group: "space", defaultValue: "4px", description: "", hook: false },
85
- { name: "--wzl-space-sm", type: "dimension", group: "space", defaultValue: "8px", description: "", hook: false },
86
- { name: "--wzl-space-md", type: "dimension", group: "space", defaultValue: "12px", description: "", hook: false },
87
- { name: "--wzl-space-lg", type: "dimension", group: "space", defaultValue: "16px", description: "", hook: false },
88
145
  { name: "--wzl-tracking-none", type: "dimension", group: "tracking", defaultValue: "0", description: "", hook: false },
89
146
  { name: "--wzl-tracking-wide", type: "dimension", group: "tracking", defaultValue: "0.06em", description: "Uppercase section headings.", hook: false },
90
147
  { name: "--wzl-tracking-wider", type: "dimension", group: "tracking", defaultValue: "0.08em", description: "Uppercase at 11px and below.", hook: false },
@@ -109,16 +166,31 @@ var TOKEN_MANIFEST = [
109
166
  { name: "--wzl-font-weight-normal", type: "fontWeight", group: "font", defaultValue: "300", description: "", hook: false },
110
167
  { name: "--wzl-font-weight-medium", type: "fontWeight", group: "font", defaultValue: "350", description: "", hook: false },
111
168
  { name: "--wzl-font-weight-bold", type: "fontWeight", group: "font", defaultValue: "400", description: "", hook: false },
169
+ { name: "--wzl-space-xs", type: "dimension", group: "space", defaultValue: "4px", description: "Alias for space-2. The t-shirt names name every other rung of the ladder.", hook: false },
170
+ { name: "--wzl-space-sm", type: "dimension", group: "space", defaultValue: "8px", description: "Alias for space-4. The t-shirt names name every other rung of the ladder.", hook: false },
171
+ { name: "--wzl-space-md", type: "dimension", group: "space", defaultValue: "12px", description: "Alias for space-6. The t-shirt names name every other rung of the ladder.", hook: false },
172
+ { name: "--wzl-space-lg", type: "dimension", group: "space", defaultValue: "16px", description: "Alias for space-8. The t-shirt names name every other rung of the ladder.", hook: false },
173
+ { name: "--wzl-disclosure-fill", type: "color", group: "disclosure", defaultValue: "var(--wzl-accent-fg)", description: "Fill of a Disclosure\u2019s fold mark.", hook: true },
112
174
  { name: "--wzl-disclosure-gap", type: "dimension", group: "disclosure", defaultValue: "4px", description: "Gap between a Disclosure\u2019s twisty and its label.", hook: true },
113
- { name: "--wzl-disclosure-target", type: "dimension", group: "disclosure", defaultValue: "20px", description: "Hit-target square of a Disclosure twisty, and of a labkit LayerList row\u2019s.", hook: true },
175
+ { name: "--wzl-disclosure-target", type: "dimension", group: "disclosure", defaultValue: "20px", description: "Hit-target square of a Disclosure twisty, and the twisty column of a LayerList row.", hook: true },
176
+ { name: "--wzl-input-surface", type: "color", group: "input", defaultValue: "var(--wzl-surface-sunken)", description: "Surface a text Input\u2019s frame sits on. Set it on any container whose own background is the default \u2014 a sunken rail or panel \u2014 where an unset field is the same color as what is behind it.", hook: true },
114
177
  { name: "--wzl-number-field-width", type: "dimension", group: "number", defaultValue: "9ch", description: "Width of a NumberField\u2019s input.", hook: true },
178
+ { name: "--wzl-params-label-align", type: "string", group: "params", defaultValue: "start", description: "Text alignment of row labels in PropertyPanel, Prefs and labkit\u2019s ControlPanel.", hook: true },
179
+ { name: "--wzl-params-label-case", type: "string", group: "params", defaultValue: "uppercase", description: "text-transform of every label and title in PropertyPanel, Prefs and labkit\u2019s ControlPanel.", hook: true },
180
+ { name: "--wzl-params-label-tracking", type: "dimension", group: "params", defaultValue: "var(--wzl-tracking-wide)", description: "Letter spacing of every label and title in PropertyPanel, Prefs and labkit\u2019s ControlPanel. Unset, rows take --wzl-tracking-wide and titles --wzl-tracking-wider.", hook: true },
181
+ { name: "--wzl-params-label-width", type: "dimension", group: "params", defaultValue: "auto", description: "Inline size of row labels in PropertyPanel, Prefs and labkit\u2019s ControlPanel.", hook: true },
115
182
  { name: "--wzl-prefs-column-width", type: "dimension", group: "prefs", defaultValue: "300px", description: "Width of the Prefs category column.", hook: true },
183
+ { name: "--wzl-prefs-rail-width", type: "dimension", group: "prefs", defaultValue: "216px", description: "Width of the Prefs navigation rail.", hook: true },
116
184
  { name: "--wzl-prop-number-width", type: "dimension", group: "prop", defaultValue: "9ch", description: "Width of a numeric field in the Properties panel.", hook: true },
185
+ { name: "--wzl-prop-row-h", type: "dimension", group: "prop", defaultValue: "20px", description: "Floor under every Properties row, whatever control it holds. Unset, it is the panel\u2019s own field height, so it follows density \u2014 20px at the default one.", hook: true },
117
186
  { name: "--wzl-prop-text-width", type: "dimension", group: "prop", defaultValue: "16ch", description: "Width of a text field in the Properties panel.", hook: true },
118
- { name: "--wzl-property-readout-w", type: "dimension", group: "property", defaultValue: "2.8em", description: "Minimum width of a Properties row\u2019s value readout.", hook: true },
187
+ { name: "--wzl-property-readout-w", type: "dimension", group: "property", defaultValue: "calc(4.5ch + 2px)", description: "Width of a Properties row\u2019s value readout \u2014 four digits, so the controls beside a column of readouts end on one line. A range whose widest value needs more widens its own readout.", hook: true },
188
+ { name: "--wzl-select-border", type: "border", group: "select", defaultValue: "1px solid var(--wzl-border)", description: "Border of a Select\u2019s trigger. Unset, a boxed trigger draws --wzl-border and a bare one a transparent border of the same width.", hook: true },
189
+ { name: "--wzl-select-fg", type: "color", group: "select", defaultValue: "var(--wzl-fg)", description: "Text color of a Select\u2019s trigger.", hook: true },
119
190
  { name: "--wzl-swatch-size", type: "dimension", group: "swatch", defaultValue: "28px", description: "Minimum cell of a swatch grid \u2014 PatternPicker, and any panel laying swatches out the same way.", hook: true },
120
191
  { name: "--wzl-timeline-label-w", type: "dimension", group: "timeline", defaultValue: "120px", description: "Width of the Timeline\u2019s track-label gutter.", hook: true },
121
- { name: "--wzl-timeline-value-axis-w", type: "dimension", group: "timeline", defaultValue: "32px", description: "Width reserved for the Timeline\u2019s value axis.", hook: true }
192
+ { name: "--wzl-timeline-value-axis-w", type: "dimension", group: "timeline", defaultValue: "32px", description: "Width reserved for the Timeline\u2019s value axis.", hook: true },
193
+ { name: "--wzl-tree-indent", type: "dimension", group: "tree", defaultValue: "calc(var(--wzl-control-h-xs) + var(--wzl-space-2))", description: "Indent of each Tree level. Unset, it is one twisty and one gap, so a child\u2019s twisty sits under its parent\u2019s label.", hook: true }
122
194
  ];
123
195
 
124
196
  // src/tokenPx.ts
@@ -163,29 +235,122 @@ function flattenTokens(doc) {
163
235
  }
164
236
 
165
237
  // src/loadDTCG.ts
238
+ var isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
239
+ function readLayer(src) {
240
+ const modes = isRecord(src.modes) ? src.modes : {};
241
+ return {
242
+ primitives: flattenTokens(isRecord(src.primitives) ? src.primitives : {}),
243
+ modes: Object.fromEntries(Object.entries(modes).map(([m, g]) => [m, flattenTokens(isRecord(g) ? g : {})]))
244
+ };
245
+ }
246
+ function readAxesExtension(raw) {
247
+ if (raw === void 0) return void 0;
248
+ if (!isRecord(raw) || !isRecord(raw.axes)) throw new Error(`DTCG "$extensions.${AXES_EXT}" needs an "axes" object`);
249
+ return {
250
+ axes: raw.axes,
251
+ varies: isRecord(raw.varies) ? raw.varies : {},
252
+ overrides: isRecord(raw.overrides) ? raw.overrides : {}
253
+ };
254
+ }
166
255
  function loadDTCG(doc) {
167
256
  if (typeof doc.name !== "string" || doc.name === "") {
168
257
  throw new Error('DTCG document needs a string "name"');
169
258
  }
170
259
  const base = doc.extends === void 0 ? weaselTheme : doc.extends;
171
- const primitives = flattenTokens(doc.primitives ?? {});
172
- const modeNames = Object.keys(doc.modes ?? {});
173
- const modes = Object.fromEntries(modeNames.map((m) => [m, flattenTokens(doc.modes[m])]));
174
- const tokens = { ...primitives };
175
- for (const name of new Set(modeNames.flatMap((m) => Object.keys(modes[m])))) {
260
+ const ext = readAxesExtension(doc.$extensions?.[AXES_EXT]);
261
+ const plain = readLayer(doc);
262
+ const overrides = Object.fromEntries(Object.entries(ext?.overrides ?? {}).map(([k, l]) => [k, readLayer(l)]));
263
+ const modeNames = [...new Set([plain, ...Object.values(overrides)].flatMap((l) => Object.keys(l.modes)))];
264
+ const tokenIn = (layer, name) => {
265
+ if (!modeNames.some((m) => layer.modes[m]?.[name])) return layer.primitives[name];
176
266
  const branch = { by: "mode" };
177
267
  for (const m of modeNames) {
178
- const t = modes[m][name] ?? primitives[name];
268
+ const t = layer.modes[m]?.[name] ?? layer.primitives[name];
179
269
  if (t) branch[m] = t;
180
270
  }
181
- tokens[name] = branch;
182
- }
271
+ return branch;
272
+ };
183
273
  const inherited = base ? themeAxes(base).mode?.default : void 0;
184
- const defaultMode = typeof doc.defaultMode === "string" ? doc.defaultMode : inherited ?? "dark";
185
- const axes = modeNames.length > 0 ? { mode: { default: defaultMode, values: Object.fromEntries(modeNames.map((m) => [m, {}])) } } : {};
274
+ const defaultMode = typeof doc.defaultMode === "string" ? doc.defaultMode : ext?.axes.mode?.default ?? inherited ?? "dark";
275
+ const modeAxis = {
276
+ default: defaultMode,
277
+ values: Object.fromEntries(modeNames.map((m) => [m, ext?.axes.mode?.values[m] ?? {}]))
278
+ };
279
+ const axes = {};
280
+ for (const [name, def] of Object.entries(ext?.axes ?? { mode: modeAxis })) {
281
+ if (name !== "mode") axes[name] = def;
282
+ else if (modeNames.length > 0) axes.mode = modeAxis;
283
+ }
284
+ if (modeNames.length > 0) axes.mode ??= modeAxis;
285
+ const all = mergeAxes(base ? themeAxes(base) : {}, axes);
286
+ const nest = (name, vary, combo) => {
287
+ if (vary.length === 0) {
288
+ const key = overrideKey(all, combo);
289
+ const layer = key === "" ? plain : overrides[key];
290
+ return layer && tokenIn(layer, name);
291
+ }
292
+ const [axis, ...rest] = vary;
293
+ const out = { by: axis };
294
+ let any = false;
295
+ for (const value of Object.keys(all[axis].values)) {
296
+ const v = nest(name, rest, { ...combo, [axis]: value });
297
+ if (v !== void 0) {
298
+ out[value] = v;
299
+ any = true;
300
+ }
301
+ }
302
+ return any ? out : void 0;
303
+ };
304
+ const tokens = {};
305
+ const names = /* @__PURE__ */ new Set([...Object.keys(plain.primitives), ...modeNames.flatMap((m) => Object.keys(plain.modes[m] ?? {}))]);
306
+ for (const name of Object.keys(ext?.varies ?? {})) names.add(name);
307
+ for (const name of names) {
308
+ const vary = (ext?.varies[name] ?? []).filter((a) => a in all);
309
+ const v = vary.length > 0 ? nest(name, vary, {}) : tokenIn(plain, name);
310
+ if (v !== void 0) tokens[name] = v;
311
+ }
186
312
  return { name: doc.name, extends: base, axes, tokens };
187
313
  }
188
314
 
189
- export { TOKEN_MANIFEST, loadDTCG, tokenPx };
315
+ // src/panel.ts
316
+ var STANCES = ["scope", "aside", "advanced", "debug", "danger", "notice", "important", "preview"];
317
+ var STANCE_SLOTS = [
318
+ { name: "surface", drives: "background" },
319
+ { name: "border-color", drives: "border-color" },
320
+ { name: "border-style", drives: "border-style" },
321
+ { name: "border-width", drives: "border-width" },
322
+ { name: "radius", drives: "border-radius" },
323
+ { name: "pad", drives: "padding" },
324
+ { name: "blur", drives: "backdrop-filter" },
325
+ { name: "tone-mix", drives: "how much of the tone mixes into the surface" },
326
+ { name: "tone", drives: "the tone a surface takes when it names none" },
327
+ // Only a surface that names its own tone recolors its controls.
328
+ { name: "accent", drives: "the accent the controls inside a surface with a tone take; unset, the tone" },
329
+ { name: "title-font", drives: "font-family" },
330
+ { name: "title-weight", drives: "font-weight" },
331
+ { name: "title-size", drives: "font-size" },
332
+ { name: "title-case", drives: "text-transform" },
333
+ { name: "title-tracking", drives: "letter-spacing" },
334
+ { name: "title-color", drives: "color" },
335
+ { name: "title-inset", drives: "the title\u2019s padding" }
336
+ ];
337
+ function stanceSlotNames() {
338
+ return STANCES.flatMap(
339
+ (stance) => STANCE_SLOTS.flatMap((slot) => [`--wzl-stance-${stance}-${slot.name}`, `--wzl-stance-${stance}-nested-${slot.name}`])
340
+ );
341
+ }
342
+ function resolveStanceSlots(tokens, base, { stance, nested, stanced } = {}) {
343
+ const out = { ...base };
344
+ if (stance === void 0) return out;
345
+ for (const slot of Object.keys(base)) {
346
+ const own = tokens[`--wzl-stance-${stance}-${slot}`];
347
+ const inner = nested ? tokens[`--wzl-stance-${stance}-nested-${slot}`] : void 0;
348
+ const fallback = slot === "tone" ? base.surface ?? base[slot] : stanced?.[slot] ?? base[slot];
349
+ out[slot] = inner ?? own ?? fallback;
350
+ }
351
+ return out;
352
+ }
353
+
354
+ export { STANCES, STANCE_SLOTS, TOKEN_MANIFEST, loadDTCG, resolveStanceSlots, stanceSlotNames, tokenPx };
190
355
  //# sourceMappingURL=index.js.map
191
356
  //# sourceMappingURL=index.js.map