@dryui/theme-wizard 3.0.0 → 5.0.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.
Files changed (47) hide show
  1. package/package.json +5 -5
  2. package/dist/actions.d.ts +0 -4
  3. package/dist/actions.js +0 -9
  4. package/dist/components/AlphaSlider.svelte +0 -13
  5. package/dist/components/AlphaSlider.svelte.d.ts +0 -9
  6. package/dist/components/ContrastBadge.svelte +0 -22
  7. package/dist/components/ContrastBadge.svelte.d.ts +0 -8
  8. package/dist/components/HsbPicker.svelte +0 -304
  9. package/dist/components/HsbPicker.svelte.d.ts +0 -9
  10. package/dist/components/StepIndicator.svelte +0 -87
  11. package/dist/components/StepIndicator.svelte.d.ts +0 -7
  12. package/dist/components/TokenPreview.svelte +0 -55
  13. package/dist/components/TokenPreview.svelte.d.ts +0 -8
  14. package/dist/components/WizardShell.svelte +0 -140
  15. package/dist/components/WizardShell.svelte.d.ts +0 -15
  16. package/dist/engine/derivation.d.ts +0 -282
  17. package/dist/engine/derivation.js +0 -1445
  18. package/dist/engine/derivation.test.d.ts +0 -1
  19. package/dist/engine/derivation.test.js +0 -956
  20. package/dist/engine/export-css.d.ts +0 -32
  21. package/dist/engine/export-css.js +0 -90
  22. package/dist/engine/export-css.test.d.ts +0 -1
  23. package/dist/engine/export-css.test.js +0 -78
  24. package/dist/engine/index.d.ts +0 -10
  25. package/dist/engine/index.js +0 -6
  26. package/dist/engine/palette.d.ts +0 -16
  27. package/dist/engine/palette.js +0 -44
  28. package/dist/engine/presets.d.ts +0 -6
  29. package/dist/engine/presets.js +0 -34
  30. package/dist/engine/url-codec.d.ts +0 -53
  31. package/dist/engine/url-codec.js +0 -243
  32. package/dist/engine/url-codec.test.d.ts +0 -1
  33. package/dist/engine/url-codec.test.js +0 -137
  34. package/dist/index.d.ts +0 -14
  35. package/dist/index.js +0 -17
  36. package/dist/state.svelte.d.ts +0 -104
  37. package/dist/state.svelte.js +0 -574
  38. package/dist/steps/BrandColor.svelte +0 -218
  39. package/dist/steps/BrandColor.svelte.d.ts +0 -6
  40. package/dist/steps/Personality.svelte +0 -319
  41. package/dist/steps/Personality.svelte.d.ts +0 -3
  42. package/dist/steps/PreviewExport.svelte +0 -115
  43. package/dist/steps/PreviewExport.svelte.d.ts +0 -9
  44. package/dist/steps/Shape.svelte +0 -121
  45. package/dist/steps/Shape.svelte.d.ts +0 -18
  46. package/dist/steps/Typography.svelte +0 -115
  47. package/dist/steps/Typography.svelte.d.ts +0 -18
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dryui/theme-wizard",
3
- "version": "3.0.0",
3
+ "version": "5.0.0",
4
4
  "author": "Rob Balfre",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -37,15 +37,15 @@
37
37
  "check": "svelte-check --tsconfig ./tsconfig.json"
38
38
  },
39
39
  "peerDependencies": {
40
- "@dryui/primitives": "^0.3.0",
41
- "@dryui/ui": "^0.3.0",
40
+ "@dryui/primitives": "^0.4.0",
41
+ "@dryui/ui": "^0.5.0",
42
42
  "lucide-svelte": ">=1.0.1",
43
43
  "svelte": "^5.55.1"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@dryui/lint": "^0.2.0",
47
- "@dryui/primitives": "^0.3.0",
48
- "@dryui/ui": "^0.3.0",
47
+ "@dryui/primitives": "^0.4.0",
48
+ "@dryui/ui": "^0.5.0",
49
49
  "lucide-svelte": "^1.0.1",
50
50
  "svelte": "^5.55.3",
51
51
  "@sveltejs/package": "^2.5.7",
package/dist/actions.d.ts DELETED
@@ -1,4 +0,0 @@
1
- /** Svelte action that sets `node.style.background` and updates it reactively. */
2
- export declare function bg(node: HTMLElement, color: string): {
3
- update(color: string): void;
4
- };
package/dist/actions.js DELETED
@@ -1,9 +0,0 @@
1
- /** Svelte action that sets `node.style.background` and updates it reactively. */
2
- export function bg(node, color) {
3
- node.style.background = color;
4
- return {
5
- update(color) {
6
- node.style.background = color;
7
- }
8
- };
9
- }
@@ -1,13 +0,0 @@
1
- <script lang="ts">
2
- import { AlphaSlider } from '@dryui/ui/alpha-slider';
3
-
4
- interface Props {
5
- value?: number;
6
- color?: string;
7
- onchange?: (value: number) => void;
8
- }
9
-
10
- let { value = $bindable(50), color = 'hsl(230, 65%, 55%)', onchange }: Props = $props();
11
- </script>
12
-
13
- <AlphaSlider bind:value {color} {...onchange ? { onchange } : {}} />
@@ -1,9 +0,0 @@
1
- import { AlphaSlider } from '@dryui/ui/alpha-slider';
2
- interface Props {
3
- value?: number;
4
- color?: string;
5
- onchange?: (value: number) => void;
6
- }
7
- declare const AlphaSlider: import("svelte").Component<Props, {}, "value">;
8
- type AlphaSlider = ReturnType<typeof AlphaSlider>;
9
- export default AlphaSlider;
@@ -1,22 +0,0 @@
1
- <script lang="ts">
2
- import { Badge } from '@dryui/ui/badge';
3
- import { contrastBetweenCssColors } from '../engine/derivation.js';
4
-
5
- interface Props {
6
- foreground: string;
7
- background: string;
8
- threshold?: number;
9
- }
10
-
11
- let { foreground, background, threshold = 4.5 }: Props = $props();
12
-
13
- let ratio = $derived(contrastBetweenCssColors(foreground, background));
14
-
15
- let passes = $derived(ratio !== null && ratio >= threshold);
16
- let ratioLabel = $derived(ratio !== null ? `${ratio.toFixed(1)}:1` : '---');
17
- let badgeColor = $derived<'success' | 'danger' | 'gray'>(
18
- ratio === null ? 'gray' : passes ? 'success' : 'danger'
19
- );
20
- </script>
21
-
22
- <Badge variant="soft" color={badgeColor} size="sm">{ratioLabel}</Badge>
@@ -1,8 +0,0 @@
1
- interface Props {
2
- foreground: string;
3
- background: string;
4
- threshold?: number;
5
- }
6
- declare const ContrastBadge: import("svelte").Component<Props, {}, "">;
7
- type ContrastBadge = ReturnType<typeof ContrastBadge>;
8
- export default ContrastBadge;
@@ -1,304 +0,0 @@
1
- <script lang="ts">
2
- import { Slider } from '@dryui/ui/slider';
3
- import { NumberInput } from '@dryui/ui/number-input';
4
- import { Input } from '@dryui/ui/input';
5
- import { Field } from '@dryui/ui/field';
6
- import { Label } from '@dryui/ui/label';
7
- import { hsbToHsl, hslToHex, hexToHsl, hslToHsb } from '../engine/derivation.js';
8
-
9
- interface Props {
10
- h?: number;
11
- s?: number;
12
- b?: number;
13
- onchange?: (h: number, s: number, b: number) => void;
14
- }
15
-
16
- let { h = $bindable(230), s = $bindable(65), b = $bindable(85), onchange }: Props = $props();
17
-
18
- let canvas: HTMLCanvasElement | undefined = $state();
19
- let isDragging = $state(false);
20
-
21
- // Derived hex value from current HSB
22
- let hexValue = $derived.by(() => {
23
- const hsl = hsbToHsl(h, s / 100, b / 100);
24
- return hslToHex(hsl.h, hsl.s, hsl.l);
25
- });
26
-
27
- // Derived preview color as CSS hsl string
28
- let previewColor = $derived.by(() => {
29
- const hsl = hsbToHsl(h, s / 100, b / 100);
30
- return `hsl(${Math.round(hsl.h)}, ${Math.round(hsl.s * 100)}%, ${Math.round(hsl.l * 100)}%)`;
31
- });
32
-
33
- // Pure hue color for canvas gradient (full saturation, full brightness)
34
- let hueColor = $derived.by(() => {
35
- const hsl = hsbToHsl(h, 1, 1);
36
- return `hsl(${Math.round(hsl.h)}, ${Math.round(hsl.s * 100)}%, ${Math.round(hsl.l * 100)}%)`;
37
- });
38
-
39
- function drawCanvas() {
40
- if (!canvas) return;
41
- const ctx = canvas.getContext('2d');
42
- if (!ctx) return;
43
-
44
- const width = canvas.width;
45
- const height = canvas.height;
46
-
47
- // Horizontal gradient: white -> full hue color
48
- const hGrad = ctx.createLinearGradient(0, 0, width, 0);
49
- hGrad.addColorStop(0, '#ffffff');
50
- hGrad.addColorStop(1, hueColor);
51
- ctx.fillStyle = hGrad;
52
- ctx.fillRect(0, 0, width, height);
53
-
54
- // Vertical gradient: transparent -> black (overlay)
55
- const vGrad = ctx.createLinearGradient(0, 0, 0, height);
56
- vGrad.addColorStop(0, 'rgba(0,0,0,0)');
57
- vGrad.addColorStop(1, 'rgba(0,0,0,1)');
58
- ctx.fillStyle = vGrad;
59
- ctx.fillRect(0, 0, width, height);
60
- }
61
-
62
- $effect(() => {
63
- // Redraw canvas when hue changes (hueColor is derived from h)
64
- void hueColor;
65
- drawCanvas();
66
- });
67
-
68
- $effect(() => {
69
- // Initial draw once canvas is mounted
70
- if (canvas) {
71
- drawCanvas();
72
- }
73
- });
74
-
75
- function getPositionFromEvent(e: PointerEvent): { x: number; y: number } {
76
- if (!canvas) return { x: 0, y: 0 };
77
- const rect = canvas.getBoundingClientRect();
78
- const x = Math.max(0, Math.min(e.clientX - rect.left, rect.width));
79
- const y = Math.max(0, Math.min(e.clientY - rect.top, rect.height));
80
- return { x, y };
81
- }
82
-
83
- function updateFromCanvasPosition(x: number, y: number) {
84
- if (!canvas) return;
85
- const newS = Math.round((x / canvas.width) * 100);
86
- const newB = Math.round((1 - y / canvas.height) * 100);
87
- s = Math.max(0, Math.min(100, newS));
88
- b = Math.max(0, Math.min(100, newB));
89
- onchange?.(h, s, b);
90
- }
91
-
92
- function handlePointerDown(e: PointerEvent) {
93
- if (!canvas) return;
94
- isDragging = true;
95
- canvas.setPointerCapture(e.pointerId);
96
- const { x, y } = getPositionFromEvent(e);
97
- updateFromCanvasPosition(x, y);
98
- }
99
-
100
- function handlePointerMove(e: PointerEvent) {
101
- if (!isDragging) return;
102
- const { x, y } = getPositionFromEvent(e);
103
- updateFromCanvasPosition(x, y);
104
- }
105
-
106
- function handlePointerUp(e: PointerEvent) {
107
- isDragging = false;
108
- if (canvas) canvas.releasePointerCapture(e.pointerId);
109
- }
110
-
111
- function handleHexInput(val: string) {
112
- const hex = val.trim();
113
- if (/^#[0-9a-fA-F]{6}$/.test(hex)) {
114
- try {
115
- const hsl = hexToHsl(hex);
116
- const hsb = hslToHsb(hsl.h, hsl.s, hsl.l);
117
- h = Math.round(hsb.h);
118
- s = Math.round(hsb.s * 100);
119
- b = Math.round(hsb.b * 100);
120
- onchange?.(h, s, b);
121
- } catch {
122
- // invalid hex, ignore
123
- }
124
- }
125
- }
126
-
127
- // Crosshair position on canvas
128
- let crosshairX = $derived((s / 100) * 256);
129
- let crosshairY = $derived((1 - b / 100) * 256);
130
-
131
- function crosshairPosition(node: HTMLElement) {
132
- $effect(() => {
133
- node.style.setProperty('left', `${crosshairX}px`);
134
- node.style.setProperty('top', `${crosshairY}px`);
135
- });
136
- }
137
-
138
- function swatchBackground(node: HTMLElement) {
139
- $effect(() => {
140
- node.style.setProperty('background', previewColor);
141
- });
142
- }
143
-
144
- // Local bindable values for number inputs
145
- let hValue = $state(h);
146
- let sValue = $state(s);
147
- let bValue = $state(b);
148
- let hexInputValue = $state('');
149
-
150
- // Sync external changes to local state
151
- $effect(() => {
152
- hValue = h;
153
- });
154
- $effect(() => {
155
- sValue = s;
156
- });
157
- $effect(() => {
158
- bValue = b;
159
- });
160
- $effect(() => {
161
- hexInputValue = hexValue;
162
- });
163
-
164
- // React to local number input changes
165
- $effect(() => {
166
- const clamped = Math.max(0, Math.min(360, hValue));
167
- if (clamped !== h) {
168
- h = clamped;
169
- onchange?.(h, s, b);
170
- }
171
- });
172
- $effect(() => {
173
- const clamped = Math.max(0, Math.min(100, sValue));
174
- if (clamped !== s) {
175
- s = clamped;
176
- onchange?.(h, s, b);
177
- }
178
- });
179
- $effect(() => {
180
- const clamped = Math.max(0, Math.min(100, bValue));
181
- if (clamped !== b) {
182
- b = clamped;
183
- onchange?.(h, s, b);
184
- }
185
- });
186
- </script>
187
-
188
- <div class="hsb-picker-root">
189
- <div class="canvas-wrapper">
190
- <canvas
191
- bind:this={canvas}
192
- width={256}
193
- height={256}
194
- class="saturation-canvas"
195
- onpointerdown={handlePointerDown}
196
- onpointermove={handlePointerMove}
197
- onpointerup={handlePointerUp}
198
- ></canvas>
199
- <div class="crosshair" use:crosshairPosition></div>
200
- </div>
201
-
202
- <div class="row-slider">
203
- <Slider
204
- bind:value={h}
205
- min={0}
206
- max={360}
207
- step={1}
208
- size="sm"
209
- oninput={() => onchange?.(h, s, b)}
210
- />
211
- </div>
212
-
213
- <div class="row-fields-end">
214
- <div class="hsb-field">
215
- <Field.Root>
216
- <Label size="sm">H</Label>
217
- <NumberInput bind:value={hValue} min={0} max={360} step={1} size="sm" />
218
- </Field.Root>
219
- </div>
220
- <div class="hsb-field">
221
- <Field.Root>
222
- <Label size="sm">S</Label>
223
- <NumberInput bind:value={sValue} min={0} max={100} step={1} size="sm" />
224
- </Field.Root>
225
- </div>
226
- <div class="hsb-field">
227
- <Field.Root>
228
- <Label size="sm">B</Label>
229
- <NumberInput bind:value={bValue} min={0} max={100} step={1} size="sm" />
230
- </Field.Root>
231
- </div>
232
- </div>
233
-
234
- <div class="row-swatch">
235
- <div class="preview-swatch" use:swatchBackground></div>
236
- <Input
237
- bind:value={hexInputValue}
238
- size="sm"
239
- placeholder="#000000"
240
- oninput={() => handleHexInput(hexInputValue)}
241
- />
242
- </div>
243
- </div>
244
-
245
- <style>
246
- .hsb-picker-root {
247
- display: grid;
248
- grid-template-columns: 256px;
249
- gap: var(--dry-space-2);
250
- }
251
-
252
- .row-slider {
253
- display: grid;
254
- grid-auto-flow: column;
255
- grid-auto-columns: 1fr;
256
- align-items: center;
257
- }
258
-
259
- .row-fields-end {
260
- display: grid;
261
- grid-template-columns: repeat(3, minmax(0, 1fr));
262
- align-items: end;
263
- gap: var(--dry-space-2);
264
- }
265
-
266
- .row-swatch {
267
- display: grid;
268
- grid-template-columns: var(--dry-space-8) minmax(0, 1fr);
269
- align-items: center;
270
- gap: var(--dry-space-2);
271
- }
272
-
273
- .canvas-wrapper {
274
- position: relative;
275
- height: 256px;
276
- border-radius: var(--dry-radius-md);
277
- overflow: hidden;
278
- cursor: crosshair;
279
- border: 1px solid var(--dry-color-stroke-weak);
280
- }
281
-
282
- .saturation-canvas {
283
- display: block;
284
- height: 256px;
285
- }
286
-
287
- .crosshair {
288
- position: absolute;
289
- aspect-ratio: 1;
290
- height: 12px;
291
- border-radius: 50%;
292
- border: 2px solid var(--dry-color-bg-overlay);
293
- box-shadow: 0 0 0 1px color-mix(in srgb, var(--dry-color-text-strong) 40%, transparent);
294
- transform: translate(-50%, -50%);
295
- pointer-events: none;
296
- }
297
-
298
- .preview-swatch {
299
- aspect-ratio: 1;
300
- height: var(--dry-space-8);
301
- border-radius: var(--dry-radius-md);
302
- border: 1px solid var(--dry-color-stroke-weak);
303
- }
304
- </style>
@@ -1,9 +0,0 @@
1
- interface Props {
2
- h?: number;
3
- s?: number;
4
- b?: number;
5
- onchange?: (h: number, s: number, b: number) => void;
6
- }
7
- declare const HsbPicker: import("svelte").Component<Props, {}, "b" | "h" | "s">;
8
- type HsbPicker = ReturnType<typeof HsbPicker>;
9
- export default HsbPicker;
@@ -1,87 +0,0 @@
1
- <script lang="ts">
2
- import { Stepper } from '@dryui/ui/stepper';
3
-
4
- interface Props {
5
- currentStep?: number;
6
- onstep?: (step: number) => void;
7
- }
8
-
9
- let { currentStep = 1, onstep }: Props = $props();
10
-
11
- let navEl = $state<HTMLElement | null>(null);
12
-
13
- const STEPS = [
14
- { n: 1, label: 'Personality' },
15
- { n: 2, label: 'Brand' },
16
- { n: 3, label: 'Typography' },
17
- { n: 4, label: 'Shape' },
18
- { n: 5, label: 'Preview' }
19
- ];
20
-
21
- // Stepper uses 0-based indexing; wizard uses 1-based
22
- let activeStepIndex = $derived(currentStep - 1);
23
-
24
- function handleStepClick(index: number) {
25
- onstep?.(index + 1);
26
- }
27
-
28
- $effect(() => {
29
- currentStep;
30
-
31
- if (!navEl) return;
32
-
33
- requestAnimationFrame(() => {
34
- const activeStep = navEl?.querySelector(
35
- "[aria-current='step'] [data-part='indicator-button']"
36
- );
37
- if (activeStep instanceof HTMLElement) {
38
- activeStep.scrollIntoView({
39
- block: 'nearest',
40
- inline: 'center'
41
- });
42
- }
43
- });
44
- });
45
- </script>
46
-
47
- <nav bind:this={navEl} class="wizard-stepper" aria-label="Wizard steps">
48
- <Stepper.Root activeStep={activeStepIndex}>
49
- <Stepper.List>
50
- {#each STEPS as step, i (step.n)}
51
- {#if i > 0}
52
- <Stepper.Separator step={i} />
53
- {/if}
54
- <Stepper.Step step={i} clickable onclick={handleStepClick}>
55
- {step.label}
56
- </Stepper.Step>
57
- {/each}
58
- </Stepper.List>
59
- </Stepper.Root>
60
- </nav>
61
-
62
- <style>
63
- .wizard-stepper {
64
- container-type: inline-size;
65
- overflow-x: auto;
66
- padding-bottom: var(--dry-space-1);
67
- scrollbar-width: none;
68
- display: grid;
69
- grid-template-columns: max-content;
70
- }
71
-
72
- .wizard-stepper::-webkit-scrollbar {
73
- display: none;
74
- }
75
-
76
- @container (max-width: 30rem) {
77
- .wizard-stepper {
78
- --dry-stepper-gap: var(--dry-space-1);
79
- --dry-stepper-indicator-size: 0.875rem;
80
- }
81
-
82
- /* Narrow-screen override for step label font size via CSS custom property. */
83
- .wizard-stepper {
84
- --dry-type-small-size: 0.75rem;
85
- }
86
- }
87
- </style>
@@ -1,7 +0,0 @@
1
- interface Props {
2
- currentStep?: number;
3
- onstep?: (step: number) => void;
4
- }
5
- declare const StepIndicator: import("svelte").Component<Props, {}, "">;
6
- type StepIndicator = ReturnType<typeof StepIndicator>;
7
- export default StepIndicator;
@@ -1,55 +0,0 @@
1
- <script lang="ts">
2
- interface Props {
3
- name: string;
4
- value: string;
5
- showValue?: boolean;
6
- }
7
-
8
- let { name, value, showValue = true }: Props = $props();
9
-
10
- import { bg } from '../actions';
11
-
12
- let label = $derived(name.replace(/^--dry-/, ''));
13
- </script>
14
-
15
- <div class="token-preview">
16
- <div class="swatch" use:bg={value}></div>
17
- <div class="token-info">
18
- <span class="token-name">{label}</span>
19
- {#if showValue}
20
- <span class="token-value">{value}</span>
21
- {/if}
22
- </div>
23
- </div>
24
-
25
- <style>
26
- .token-preview {
27
- display: grid;
28
- grid-template-columns: 2rem 1fr;
29
- align-items: center;
30
- gap: var(--dry-space-2);
31
- }
32
-
33
- .swatch {
34
- aspect-ratio: 1;
35
- border-radius: var(--dry-radius-sm, 4px);
36
- border: 1px solid var(--dry-color-stroke-weak);
37
- }
38
-
39
- .token-info {
40
- display: grid;
41
- gap: var(--dry-space-1);
42
- }
43
-
44
- .token-name {
45
- font-family: var(--dry-font-mono);
46
- font-size: var(--dry-type-small-size);
47
- color: var(--dry-color-text-strong);
48
- }
49
-
50
- .token-value {
51
- font-family: var(--dry-font-mono);
52
- font-size: var(--dry-type-xs-size, 0.7rem);
53
- color: var(--dry-color-text-weak);
54
- }
55
- </style>
@@ -1,8 +0,0 @@
1
- interface Props {
2
- name: string;
3
- value: string;
4
- showValue?: boolean;
5
- }
6
- declare const TokenPreview: import("svelte").Component<Props, {}, "">;
7
- type TokenPreview = ReturnType<typeof TokenPreview>;
8
- export default TokenPreview;