@weasel-js/labkit 0.8.0 → 1.0.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.
Files changed (74) hide show
  1. package/README.md +11 -3
  2. package/dist/_dts/{DrawCommand-DACv9lhe.d.ts → DrawCommand-CitWPxMY.d.ts} +80 -7
  3. package/dist/_dts/{useExperimentState-eSvpwV_P.d.ts → useExperimentState-CJn2hHzd.d.ts} +6 -4
  4. package/dist/{chunk-SSMHPMDV.js → chunk-53XSBIUK.js} +12 -11
  5. package/dist/chunk-53XSBIUK.js.map +1 -0
  6. package/dist/{chunk-OHGEOTCV.js → chunk-EXBV7A6X.js} +3 -3
  7. package/dist/{chunk-OHGEOTCV.js.map → chunk-EXBV7A6X.js.map} +1 -1
  8. package/dist/{chunk-6V7ROR7G.js → chunk-ISEK5LXT.js} +1931 -904
  9. package/dist/chunk-ISEK5LXT.js.map +1 -0
  10. package/dist/chunk-NRD3TDNQ.js +5946 -0
  11. package/dist/chunk-NRD3TDNQ.js.map +1 -0
  12. package/dist/index.d.ts +37 -13
  13. package/dist/index.js +776 -27
  14. package/dist/index.js.map +1 -1
  15. package/dist/passthrough/weasel-canvas.d.ts +29 -6
  16. package/dist/passthrough/weasel-canvas.js +1 -1
  17. package/dist/passthrough/weasel-ui.d.ts +169 -79
  18. package/dist/passthrough/weasel-ui.js +2 -2
  19. package/dist/state/index.d.ts +2 -2
  20. package/dist/state/index.js +2 -2
  21. package/dist/styles.css +228 -287
  22. package/dist/ui/layers/index.js +3 -3
  23. package/package.json +4 -6
  24. package/src/canvas/CanvasStack.less +1 -1
  25. package/src/controls/ControlPanel.less +12 -12
  26. package/src/dragdrop/DragGhost.less +2 -2
  27. package/src/dragdrop/Palette.less +6 -6
  28. package/src/index.ts +2 -0
  29. package/src/lab/Lab.test.tsx +20 -15
  30. package/src/lab/Lab.tsx +33 -32
  31. package/src/lab/LabContext.ts +3 -3
  32. package/src/lab/LabShell.less +14 -14
  33. package/src/lab/LabShell.test.tsx +7 -20
  34. package/src/lab/LabShell.tsx +22 -13
  35. package/src/lab/WorkspaceGrid.less +1 -1
  36. package/src/lab/WorkspaceGrid.stories.tsx +4 -4
  37. package/src/lab/index.ts +1 -1
  38. package/src/lab/useSystemMode.ts +30 -0
  39. package/src/layers/LayerList.less +9 -9
  40. package/src/passthrough/weasel-ui.ts +4 -0
  41. package/src/primitives/FpsMeter.less +5 -5
  42. package/src/primitives/ScaleIndicator.less +7 -7
  43. package/src/primitives/Sidebar.less +12 -12
  44. package/src/primitives/StatusBar.less +10 -10
  45. package/src/primitives/Toolbar.less +16 -16
  46. package/src/state/store.test.ts +17 -9
  47. package/src/state/store.ts +13 -10
  48. package/src/state/types.ts +5 -2
  49. package/src/theme/Interstellar.stories.less +20 -20
  50. package/src/theme/Interstellar.stories.tsx +44 -59
  51. package/src/theme/base.less +26 -26
  52. package/src/theme/interstellar.test.ts +30 -0
  53. package/src/theme/interstellar.tokens.json +57 -0
  54. package/src/theme/interstellar.ts +9 -0
  55. package/src/ui/layers/LayerStack.less +21 -21
  56. package/src/ui/properties/CurveField.less +5 -5
  57. package/src/ui/properties/EffectCard.tsx +2 -2
  58. package/src/ui/properties/PropertyGroup.less +6 -6
  59. package/src/ui/properties/PropertyPanel.less +70 -70
  60. package/src/ui/properties/PropertyPanel.stories.tsx +0 -1
  61. package/src/ui/properties/SpeechBalloonPanels.stories.tsx +1 -1
  62. package/src/ui/properties/storyLayouts.tsx +1 -1
  63. package/src/workspace/Workspace.less +14 -14
  64. package/src/workspace/Workspace.stories.tsx +2 -2
  65. package/src/workspace/Workspace.test.tsx +2 -2
  66. package/dist/chunk-6V7ROR7G.js.map +0 -1
  67. package/dist/chunk-FBZCQXI2.js +0 -6037
  68. package/dist/chunk-FBZCQXI2.js.map +0 -1
  69. package/dist/chunk-SSMHPMDV.js.map +0 -1
  70. package/dist/theme-interstellar.css +0 -25
  71. package/dist/theme-light.css +0 -13
  72. package/src/theme/interstellar.less +0 -34
  73. package/src/theme/light.less +0 -14
  74. package/src/theme/tokens.less +0 -85
@@ -1,5 +1,6 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite';
2
- import { useEffect, useState } from 'react';
2
+ import type { TokenName } from '@weasel-js/theme';
3
+ import { useTheme } from '@weasel-js/theme/react';
3
4
  import {
4
5
  CheckboxRow,
5
6
  ColorRow,
@@ -17,7 +18,7 @@ const meta: Meta = {
17
18
  description: {
18
19
  component:
19
20
  'The interstellar theme: deep-space cosmic gradient + starscape backdrop, glass surfaces, Oswald display type, purple accent. ' +
20
- 'Tokens live in `src/theme/tokens.less` (defaults) and `src/theme/interstellar.less` (explicit `.lk-theme-interstellar` overrides).',
21
+ 'Authored as DTCG in `src/theme/interstellar.tokens.json` and loaded with `loadDTCG`; it extends the built-in weasel theme, overriding values rather than adding tokens.',
21
22
  },
22
23
  },
23
24
  },
@@ -34,58 +35,40 @@ interface Swatch {
34
35
  }
35
36
 
36
37
  const SURFACE_TOKENS: Swatch[] = [
37
- { name: 'bg', cssVar: '--lk-bg' },
38
- { name: 'bg-elevated', cssVar: '--lk-bg-elevated' },
39
- { name: 'bg-canvas', cssVar: '--lk-bg-canvas' },
40
- { name: 'border', cssVar: '--lk-border' },
41
- { name: 'divider', cssVar: '--lk-divider' },
38
+ { name: 'bg', cssVar: '--wzl-surface' },
39
+ { name: 'bg-elevated', cssVar: '--wzl-surface-raised' },
40
+ { name: 'bg-canvas', cssVar: '--wzl-surface-sunken' },
41
+ { name: 'border', cssVar: '--wzl-border' },
42
+ { name: 'divider', cssVar: '--wzl-line-subtle' },
42
43
  ];
43
44
 
44
45
  const TEXT_TOKENS: Swatch[] = [
45
- { name: 'text', cssVar: '--lk-text' },
46
- { name: 'text-muted', cssVar: '--lk-text-muted' },
47
- { name: 'text-disabled', cssVar: '--lk-text-disabled' },
46
+ { name: 'text', cssVar: '--wzl-fg' },
47
+ { name: 'text-muted', cssVar: '--wzl-fg-muted' },
48
+ { name: 'text-disabled', cssVar: '--wzl-fg-subtle' },
48
49
  ];
49
50
 
50
51
  const ACCENT_TOKENS: Swatch[] = [
51
- { name: 'accent', cssVar: '--lk-accent' },
52
- { name: 'accent-hover', cssVar: '--lk-accent-hover' },
53
- { name: 'focus-ring', cssVar: '--lk-focus-ring' },
52
+ { name: 'accent', cssVar: '--wzl-accent' },
53
+ { name: 'accent-hover', cssVar: '--wzl-accent-hover' },
54
+ { name: 'focus-ring', cssVar: '--wzl-focus-ring' },
54
55
  ];
55
56
 
56
57
  const PALETTE_TOKENS: Swatch[] = [
57
- { name: 'green', cssVar: '--lk-swatch-green' },
58
- { name: 'pink', cssVar: '--lk-swatch-pink' },
59
- { name: 'cyan', cssVar: '--lk-swatch-cyan' },
60
- { name: 'gold', cssVar: '--lk-swatch-gold' },
61
- { name: 'amber', cssVar: '--lk-swatch-amber' },
62
- { name: 'violet', cssVar: '--lk-swatch-violet' },
63
- { name: 'mint', cssVar: '--lk-swatch-mint' },
64
- { name: 'sky', cssVar: '--lk-swatch-sky' },
65
- { name: 'orange', cssVar: '--lk-swatch-orange' },
66
- { name: 'magenta', cssVar: '--lk-swatch-magenta' },
58
+ { name: 'green', cssVar: '--wzl-swatch-green' },
59
+ { name: 'pink', cssVar: '--wzl-swatch-pink' },
60
+ { name: 'cyan', cssVar: '--wzl-swatch-cyan' },
61
+ { name: 'gold', cssVar: '--wzl-swatch-gold' },
62
+ { name: 'amber', cssVar: '--wzl-swatch-amber' },
63
+ { name: 'violet', cssVar: '--wzl-swatch-violet' },
64
+ { name: 'mint', cssVar: '--wzl-swatch-mint' },
65
+ { name: 'sky', cssVar: '--wzl-swatch-sky' },
66
+ { name: 'orange', cssVar: '--wzl-swatch-orange' },
67
+ { name: 'magenta', cssVar: '--wzl-swatch-magenta' },
67
68
  ];
68
69
 
69
- /**
70
- * Resolves a CSS custom property's computed value off `document.documentElement`
71
- * so swatch cards can display the actual color alongside the var name.
72
- * Returns null until the first effect runs.
73
- */
74
- function useResolvedVars(vars: readonly string[]): Record<string, string> {
75
- const key = vars.join('|');
76
- const [resolved, setResolved] = useState<Record<string, string>>({});
77
- useEffect(() => {
78
- const root = document.querySelector('.lk-theme-interstellar') ?? document.documentElement;
79
- const cs = window.getComputedStyle(root);
80
- const out: Record<string, string> = {};
81
- for (const v of key.split('|')) out[v] = cs.getPropertyValue(v).trim();
82
- setResolved(out);
83
- }, [key]);
84
- return resolved;
85
- }
86
-
87
70
  function SwatchGrid({ tokens, title }: { tokens: readonly Swatch[]; title: string }) {
88
- const resolved = useResolvedVars(tokens.map((t) => t.cssVar));
71
+ const { resolved } = useTheme();
89
72
  return (
90
73
  <div>
91
74
  <h3 className="lk-theme-doc__heading">{title}</h3>
@@ -96,7 +79,7 @@ function SwatchGrid({ tokens, title }: { tokens: readonly Swatch[]; title: strin
96
79
  <div className="lk-theme-doc__swatch-meta">
97
80
  <div className="lk-theme-doc__swatch-name">{t.name}</div>
98
81
  <code className="lk-theme-doc__swatch-var">{t.cssVar}</code>
99
- <code className="lk-theme-doc__swatch-value">{resolved[t.cssVar] ?? '…'}</code>
82
+ <code className="lk-theme-doc__swatch-value">{resolved[t.cssVar as TokenName]}</code>
100
83
  </div>
101
84
  </div>
102
85
  ))}
@@ -108,8 +91,8 @@ function SwatchGrid({ tokens, title }: { tokens: readonly Swatch[]; title: strin
108
91
  function NebulaPreview() {
109
92
  return (
110
93
  <div>
111
- <h3 className="lk-theme-doc__heading">Cosmic backdrop (`--lk-space-nebula`)</h3>
112
- <div className="lk-theme-doc__nebula" style={{ background: 'var(--lk-space-nebula)' }} />
94
+ <h3 className="lk-theme-doc__heading">Cosmic backdrop (`--wzl-backdrop`)</h3>
95
+ <div className="lk-theme-doc__nebula" style={{ background: 'var(--wzl-backdrop)' }} />
113
96
  <p className="lk-theme-doc__note">
114
97
  Default: four radial gradients (purple, pink, blue, then a dark void base). Override per-Lab
115
98
  by passing <code>nebula={'{[colors]}'}</code> to the <code>&lt;Lab&gt;</code> component.
@@ -123,29 +106,29 @@ function TypographySample() {
123
106
  <div>
124
107
  <h3 className="lk-theme-doc__heading">Typography</h3>
125
108
  <div className="lk-theme-doc__type-stack">
126
- <h1 style={{ font: '300 2.6rem/1 var(--lk-font-display)', color: 'var(--lk-text)' }}>
109
+ <h1 style={{ font: '300 2.6rem/1 var(--wzl-font-display)', color: 'var(--wzl-fg)' }}>
127
110
  Heading display 300
128
111
  </h1>
129
- <h2 style={{ font: '300 1.4rem/1.2 var(--lk-font-display)', color: 'var(--lk-text)' }}>
112
+ <h2 style={{ font: '300 1.4rem/1.2 var(--wzl-font-display)', color: 'var(--wzl-fg)' }}>
130
113
  Heading 300
131
114
  </h2>
132
115
  <div
133
116
  style={{
134
- font: '300 0.78rem/1 var(--lk-font-display)',
135
- color: 'var(--lk-text-muted)',
117
+ font: '300 0.78rem/1 var(--wzl-font-display)',
118
+ color: 'var(--wzl-fg-muted)',
136
119
  textTransform: 'uppercase',
137
120
  letterSpacing: '0.05em',
138
121
  }}
139
122
  >
140
123
  Label uppercase 300
141
124
  </div>
142
- <p style={{ font: '400 0.9rem/1.5 var(--lk-font)', color: 'var(--lk-text)' }}>
125
+ <p style={{ font: '400 0.9rem/1.5 var(--wzl-font-ui)', color: 'var(--wzl-fg)' }}>
143
126
  Body 400. Free-form prose at the default font size and line height.
144
127
  </p>
145
128
  <code
146
129
  style={{
147
- font: '400 0.85rem/1 var(--lk-font-mono)',
148
- color: 'var(--lk-text)',
130
+ font: '400 0.85rem/1 var(--wzl-font-mono)',
131
+ color: 'var(--wzl-fg)',
149
132
  background: 'rgba(0,0,0,0.35)',
150
133
  padding: '4px 8px',
151
134
  borderRadius: 3,
@@ -188,20 +171,21 @@ function LivePanelPreview() {
188
171
  export const Overview: Story = {
189
172
  render: () => (
190
173
  <div
191
- className="lk-theme-interstellar lk-theme-doc"
174
+ className="lk-theme-doc"
192
175
  style={{
193
- background: 'var(--lk-space-nebula)',
176
+ backgroundColor: 'var(--wzl-surface)',
177
+ backgroundImage: 'var(--wzl-backdrop)',
194
178
  padding: 32,
195
179
  minHeight: '100vh',
196
180
  }}
197
181
  >
198
- <h1 style={{ font: '300 2.6rem/1 var(--lk-font-display)', color: 'var(--lk-text)' }}>
182
+ <h1 style={{ font: '300 2.6rem/1 var(--wzl-font-display)', color: 'var(--wzl-fg)' }}>
199
183
  Interstellar
200
184
  </h1>
201
185
  <p
202
186
  style={{
203
- font: '300 1.05rem/1.4 var(--lk-font-display)',
204
- color: 'var(--lk-text-muted)',
187
+ font: '300 1.05rem/1.4 var(--wzl-font-display)',
188
+ color: 'var(--wzl-fg-muted)',
205
189
  maxWidth: 640,
206
190
  margin: '12px 0 32px',
207
191
  }}
@@ -225,9 +209,10 @@ export const Overview: Story = {
225
209
  export const Palette: Story = {
226
210
  render: () => (
227
211
  <div
228
- className="lk-theme-interstellar lk-theme-doc"
212
+ className="lk-theme-doc"
229
213
  style={{
230
- background: 'var(--lk-space-nebula)',
214
+ backgroundColor: 'var(--wzl-surface)',
215
+ backgroundImage: 'var(--wzl-backdrop)',
231
216
  padding: 32,
232
217
  minHeight: '100vh',
233
218
  }}
@@ -1,5 +1,3 @@
1
- @import './tokens.less';
2
-
3
1
  // Self-hosted Oswald (variable font, wght axis 200–700) for the headline
4
2
  // typeface. Sourced from @weasel-js/theme; build:fonts copies it into dist/fonts.
5
3
  @font-face {
@@ -18,11 +16,11 @@
18
16
 
19
17
  // Resets and global element defaults.
20
18
  .lk-root {
21
- font-family: var(--lk-font);
22
- font-size: var(--lk-font-size);
23
- font-weight: var(--lk-font-weight-light, 300);
24
- color: var(--lk-text);
25
- background: var(--lk-bg);
19
+ font-family: var(--wzl-font-ui);
20
+ font-size: var(--wzl-font-size);
21
+ font-weight: var(--wzl-font-weight-light);
22
+ color: var(--wzl-fg);
23
+ background: var(--wzl-surface);
26
24
  line-height: 1.4;
27
25
 
28
26
  *, *::before, *::after {
@@ -32,33 +30,33 @@
32
30
  button {
33
31
  font: inherit;
34
32
  color: inherit;
35
- background: var(--lk-bg-elevated);
36
- border: 1px solid var(--lk-border);
37
- border-radius: var(--lk-radius);
38
- height: var(--lk-control-height);
39
- padding: 0 var(--lk-spacing-md);
33
+ background: var(--wzl-surface-raised);
34
+ border: 1px solid var(--wzl-border);
35
+ border-radius: var(--wzl-radius-md);
36
+ height: var(--wzl-control-h);
37
+ padding: 0 var(--wzl-space-md);
40
38
  cursor: pointer;
41
- backdrop-filter: blur(var(--lk-glass-blur, 0));
42
- -webkit-backdrop-filter: blur(var(--lk-glass-blur, 0));
39
+ backdrop-filter: blur(var(--wzl-glass-blur));
40
+ -webkit-backdrop-filter: blur(var(--wzl-glass-blur));
43
41
 
44
42
  &:hover {
45
- border-color: var(--lk-accent);
46
- color: var(--lk-accent);
43
+ border-color: var(--wzl-accent);
44
+ color: var(--wzl-accent);
47
45
  }
48
46
 
49
47
  &:focus-visible {
50
- outline: 2px solid var(--lk-focus-ring);
48
+ outline: 2px solid var(--wzl-focus-ring);
51
49
  outline-offset: 1px;
52
50
  }
53
51
 
54
52
  &:disabled {
55
- color: var(--lk-text-disabled);
53
+ color: var(--wzl-fg-subtle);
56
54
  cursor: not-allowed;
57
55
  }
58
56
  }
59
57
 
60
58
  input[type="range"] {
61
- accent-color: var(--lk-accent);
59
+ accent-color: var(--wzl-accent);
62
60
  }
63
61
  }
64
62
 
@@ -67,7 +65,9 @@
67
65
  .lk-lab {
68
66
  position: relative;
69
67
  isolation: isolate;
70
- background: var(--lk-space-nebula, var(--lk-bg));
68
+ background-color: var(--wzl-surface);
69
+ // Not the shorthand: `background: none` paints transparent, not the surface.
70
+ background-image: var(--wzl-backdrop);
71
71
  background-attachment: fixed;
72
72
  }
73
73
 
@@ -102,13 +102,13 @@
102
102
  background-repeat: no-repeat;
103
103
  }
104
104
 
105
- // Light theme opts out of the cosmic backdrop & starscape.
106
- .lk-theme-light.lk-lab {
107
- background: var(--lk-bg);
105
+ // The backdrop follows the token, but a starfield is structure, not a color —
106
+ // it needs the mode.
107
+ .lk-lab[data-wzl-mode='light'] {
108
108
  background-attachment: scroll;
109
109
  }
110
110
 
111
- .lk-theme-light.lk-lab::before {
111
+ .lk-lab[data-wzl-mode='light']::before {
112
112
  display: none;
113
113
  }
114
114
 
@@ -116,8 +116,8 @@
116
116
  .lk-lab h1,
117
117
  .lk-lab h2,
118
118
  .lk-lab h3 {
119
- font-family: var(--lk-font-display);
120
- font-weight: var(--lk-font-weight-light, 300);
119
+ font-family: var(--wzl-font-display);
120
+ font-weight: var(--wzl-font-weight-light);
121
121
  letter-spacing: 0.01em;
122
122
  line-height: 1.2;
123
123
  }
@@ -0,0 +1,30 @@
1
+ import { resolveTheme } from '@weasel-js/theme';
2
+ import { describe, expect, it } from 'vitest';
3
+ import { interstellarTheme } from './interstellar';
4
+
5
+ describe('interstellarTheme', () => {
6
+ it('extends weasel and resolves both modes', () => {
7
+ const dark = resolveTheme(interstellarTheme, 'dark');
8
+ const light = resolveTheme(interstellarTheme, 'light');
9
+
10
+ // Values carried over verbatim from the retired Less.
11
+ expect(dark['--wzl-surface']).toBe('#0a0a14');
12
+ expect(dark['--wzl-accent']).toBe('#b08adb');
13
+ expect(light['--wzl-surface']).toBe('#fafaf7');
14
+ expect(light['--wzl-accent']).toBe('#a86f3c');
15
+
16
+ // Inherited from weasel — interstellar overrides values, not the token set.
17
+ expect(dark['--wzl-space-md']).toBe('12px');
18
+ expect(dark['--wzl-swatch-cyan']).toBe('#00dfff');
19
+
20
+ // Deliberate divergences from the base.
21
+ expect(dark['--wzl-radius-md']).toBe('6px');
22
+ expect(dark['--wzl-font-weight-light']).toBe('300');
23
+ expect(dark['--wzl-glass-blur']).toBe('12px');
24
+ });
25
+
26
+ it('carries the cosmic backdrop in dark and drops it in light', () => {
27
+ expect(resolveTheme(interstellarTheme, 'dark')['--wzl-backdrop']).toContain('radial-gradient');
28
+ expect(resolveTheme(interstellarTheme, 'light')['--wzl-backdrop']).toBe('none');
29
+ });
30
+ });
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "interstellar",
3
+ "defaultMode": "dark",
4
+ "primitives": {
5
+ "dimension": {
6
+ "$type": "dimension",
7
+ "radius-md": { "$value": "6px" },
8
+ "glass-blur": { "$value": "12px" }
9
+ },
10
+ "fontWeight": {
11
+ "$type": "fontWeight",
12
+ "font-weight-light": { "$value": 300 },
13
+ "font-weight-medium": { "$value": 500 },
14
+ "font-weight-bold": { "$value": 700 }
15
+ }
16
+ },
17
+ "modes": {
18
+ "dark": {
19
+ "color": {
20
+ "$type": "color",
21
+ "surface": { "$value": "#0a0a14" },
22
+ "surface-raised": { "$value": "rgba(20, 20, 36, 0.55)" },
23
+ "surface-sunken": { "$value": "#02020a" },
24
+ "border": { "$value": "rgba(255, 255, 255, 0.14)" },
25
+ "line-subtle": { "$value": "rgba(255, 255, 255, 0.08)" },
26
+ "fg": { "$value": "#e8e8f0" },
27
+ "fg-muted": { "$value": "#a8a8c0" },
28
+ "fg-subtle": { "$value": "#5a5a78" },
29
+ "accent": { "$value": "#b08adb" },
30
+ "accent-hover": { "$value": "#c6a3ee" },
31
+ "focus-ring": { "$value": "rgba(176, 138, 219, 0.45)" }
32
+ },
33
+ "gradient": {
34
+ "$type": "gradient",
35
+ "backdrop": {
36
+ "$value": "radial-gradient(ellipse 60% 80% at 18% 30%, rgba(150, 80, 200, 0.22), transparent 70%), radial-gradient(ellipse 55% 90% at 78% 70%, rgba(220, 100, 140, 0.18), transparent 65%), radial-gradient(ellipse 50% 70% at 50% 50%, rgba(60, 110, 200, 0.18), transparent 75%), radial-gradient(ellipse at center, #0a0a18 0%, #02020a 100%)"
37
+ }
38
+ }
39
+ },
40
+ "light": {
41
+ "color": {
42
+ "$type": "color",
43
+ "surface": { "$value": "#fafaf7" },
44
+ "surface-raised": { "$value": "#ffffff" },
45
+ "surface-sunken": { "$value": "#f0ebe2" },
46
+ "border": { "$value": "#c9bba5" },
47
+ "line-subtle": { "$value": "rgba(0, 0, 0, 0.08)" },
48
+ "fg": { "$value": "#2a2018" },
49
+ "fg-muted": { "$value": "#6b5c4a" },
50
+ "fg-subtle": { "$value": "#a89b85" },
51
+ "accent": { "$value": "#a86f3c" },
52
+ "accent-hover": { "$value": "#c2854f" },
53
+ "focus-ring": { "$value": "rgba(168, 111, 60, 0.35)" }
54
+ }
55
+ }
56
+ }
57
+ }
@@ -0,0 +1,9 @@
1
+ import { loadDTCG, type Theme } from '@weasel-js/theme';
2
+ import doc from './interstellar.tokens.json' with { type: 'json' };
3
+
4
+ /**
5
+ * labkit's theme: a cosmic dark and a warm parchment light, extending the
6
+ * built-in weasel theme. Authored as DTCG and loaded at import time — the
7
+ * interchange path a theme exported from a design tool would take.
8
+ */
9
+ export const interstellarTheme: Theme = loadDTCG(doc);
@@ -12,10 +12,10 @@
12
12
 
13
13
  .lk-layer-stack__title {
14
14
  margin: 0;
15
- font-family: var(--lk-font-display);
16
- font-weight: var(--lk-font-weight-light, 300);
15
+ font-family: var(--wzl-font-display);
16
+ font-weight: var(--wzl-font-weight-light);
17
17
  font-size: 1rem;
18
- color: var(--lk-text);
18
+ color: var(--wzl-fg);
19
19
  }
20
20
 
21
21
  .lk-layer-stack__palette {
@@ -25,17 +25,17 @@
25
25
 
26
26
  .lk-layer-stack__add {
27
27
  background: transparent;
28
- border: 1px dashed var(--lk-border);
29
- border-radius: var(--lk-radius-sm, 6px);
28
+ border: 1px dashed var(--wzl-border);
29
+ border-radius: var(--wzl-radius-sm);
30
30
  padding: 2px 8px;
31
31
  font-size: 0.78rem;
32
- color: var(--lk-text-dim);
32
+ color: var(--wzl-fg-subtle);
33
33
  cursor: pointer;
34
34
  }
35
35
 
36
36
  .lk-layer-stack__add:hover {
37
- background: var(--lk-bg-elevated);
38
- color: var(--lk-text);
37
+ background: var(--wzl-surface-raised);
38
+ color: var(--wzl-fg);
39
39
  }
40
40
 
41
41
  .lk-layer-stack__list {
@@ -45,29 +45,29 @@
45
45
 
46
46
  .lk-layer-stack__drop-hint {
47
47
  height: 2px;
48
- background: var(--lk-accent, #6cf);
48
+ background: var(--wzl-accent);
49
49
  border-radius: 1px;
50
50
  }
51
51
 
52
52
  .lk-layer-stack__empty {
53
- color: var(--lk-text-dim);
53
+ color: var(--wzl-fg-subtle);
54
54
  font-size: 0.85rem;
55
55
  padding: 8px;
56
56
  text-align: center;
57
57
  }
58
58
 
59
59
  .lk-layer-card {
60
- background: var(--lk-bg-elevated);
61
- border: 1px solid var(--lk-border);
62
- border-radius: var(--lk-radius-md, 8px);
60
+ background: var(--wzl-surface-raised);
61
+ border: 1px solid var(--wzl-border);
62
+ border-radius: var(--wzl-radius-md);
63
63
  overflow: hidden;
64
64
  }
65
65
 
66
66
  .lk-layer-card.has-accent {
67
- // Re-bind --lk-accent inside the card so descendant controls (slider tracks
67
+ // Re-bind --wzl-accent inside the card so descendant controls (slider tracks
68
68
  // and thumbs, readouts, etc.) tint to the per-card accent instead of the
69
69
  // global theme accent. Mirrors EffectCard's behavior.
70
- --lk-accent: var(--lk-layer-card-accent);
70
+ --wzl-accent: var(--lk-layer-card-accent);
71
71
  border-left: 3px solid var(--lk-layer-card-accent);
72
72
  }
73
73
 
@@ -87,7 +87,7 @@
87
87
  .lk-root .lk-layer-card__handle {
88
88
  background: transparent;
89
89
  border: 0;
90
- color: var(--lk-text-dim);
90
+ color: var(--wzl-fg-subtle);
91
91
  padding: 2px 4px;
92
92
  cursor: grab;
93
93
  display: inline-flex;
@@ -95,7 +95,7 @@
95
95
  justify-content: center;
96
96
  min-width: 22px;
97
97
  height: 22px;
98
- border-radius: var(--lk-radius-sm, 6px);
98
+ border-radius: var(--wzl-radius-sm);
99
99
  font-weight: 600;
100
100
  font-size: 0.85rem;
101
101
  }
@@ -118,7 +118,7 @@
118
118
  .lk-layer-card__primary {
119
119
  background: transparent;
120
120
  border: 0;
121
- color: var(--lk-text);
121
+ color: var(--wzl-fg);
122
122
  font: inherit;
123
123
  text-transform: uppercase;
124
124
  letter-spacing: 0.06em;
@@ -127,17 +127,17 @@
127
127
  .lk-root .lk-layer-card__remove {
128
128
  background: transparent;
129
129
  border: 0;
130
- color: var(--lk-text-dim);
130
+ color: var(--wzl-fg-subtle);
131
131
  font-size: 1rem;
132
132
  cursor: pointer;
133
133
  padding: 2px 6px;
134
134
  }
135
135
 
136
136
  .lk-layer-card__remove:hover {
137
- color: var(--lk-text);
137
+ color: var(--wzl-fg);
138
138
  }
139
139
 
140
140
  .lk-layer-card__body {
141
141
  padding: 8px;
142
- border-top: 1px solid var(--lk-border);
142
+ border-top: 1px solid var(--wzl-border);
143
143
  }
@@ -20,7 +20,7 @@
20
20
  justify-content: space-between;
21
21
  font-variant-numeric: tabular-nums;
22
22
  font-size: 0.85rem;
23
- color: var(--lk-text-dim);
23
+ color: var(--wzl-fg-subtle);
24
24
  }
25
25
 
26
26
  .lk-curve-field__readout {
@@ -35,14 +35,14 @@
35
35
 
36
36
  .lk-curve-field__action {
37
37
  background: transparent;
38
- border: 1px solid var(--lk-border);
39
- border-radius: var(--lk-radius-sm, 6px);
40
- color: var(--lk-text);
38
+ border: 1px solid var(--wzl-border);
39
+ border-radius: var(--wzl-radius-sm);
40
+ color: var(--wzl-fg);
41
41
  padding: 4px 10px;
42
42
  font-size: 0.78rem;
43
43
  cursor: pointer;
44
44
  }
45
45
 
46
46
  .lk-curve-field__action:hover {
47
- background: var(--lk-bg-elevated);
47
+ background: var(--wzl-surface-raised);
48
48
  }
@@ -26,7 +26,7 @@ export function Subpanel({ title, children, className }: SubpanelProps) {
26
26
 
27
27
  // ── EffectCard ───────────────────────────────────────────────────────
28
28
  // Draggable, collapsible card for stacked effect editors. Optional --lk-panel-accent
29
- // per-instance recolors the title bar, border-left, and (via re-binding --lk-accent
29
+ // per-instance recolors the title bar, border-left, and (via re-binding --wzl-accent
30
30
  // inside the card) every descendant control that reads from the accent token.
31
31
 
32
32
  export interface EffectCardProps {
@@ -82,7 +82,7 @@ export function EffectCard({
82
82
  const stateClass = `${expanded ? 'is-expanded' : 'is-collapsed'}${dragging ? ' is-dragging' : ''}${accent ? ' has-accent' : ''}`;
83
83
  const cls = `lk-effect-card ${stateClass}${className ? ` ${className}` : ''}`;
84
84
  // Per-instance dynamic color → CSS custom property requires inline style.
85
- // The card's CSS re-binds --lk-accent to --lk-panel-accent so descendants tint.
85
+ // The card's CSS re-binds --wzl-accent to --lk-panel-accent so descendants tint.
86
86
  const style = accent ? ({ '--lk-panel-accent': accent } as CSSProperties) : undefined;
87
87
 
88
88
  // When a toggle handler is supplied the header acts as a disclosure button:
@@ -1,7 +1,7 @@
1
1
  .lk-property-group {
2
2
  grid-column: 1 / -1;
3
- background: var(--lk-bg-sunken, rgba(0, 0, 0, 0.18));
4
- border-radius: var(--lk-radius-md, 8px);
3
+ background: var(--wzl-surface-sunken);
4
+ border-radius: var(--wzl-radius-md);
5
5
  padding: 10px 12px 12px;
6
6
  }
7
7
 
@@ -11,17 +11,17 @@
11
11
  align-items: center;
12
12
  gap: 10px;
13
13
  margin: 0 0 8px;
14
- font-family: var(--lk-font-display);
15
- font-weight: var(--lk-font-weight-light, 300);
14
+ font-family: var(--wzl-font-display);
15
+ font-weight: var(--wzl-font-weight-light);
16
16
  font-size: 0.78rem;
17
17
  letter-spacing: 0.08em;
18
18
  text-transform: uppercase;
19
- color: var(--lk-text-dim);
19
+ color: var(--wzl-fg-subtle);
20
20
  }
21
21
 
22
22
  .lk-property-group__title hr {
23
23
  border: 0;
24
- border-top: 1px solid var(--lk-border);
24
+ border-top: 1px solid var(--wzl-border);
25
25
  margin: 0;
26
26
  }
27
27