@dryui/theme-wizard 1.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.
- package/dist/actions.d.ts +4 -0
- package/dist/actions.js +9 -0
- package/dist/components/AlphaSlider.svelte +13 -0
- package/dist/components/AlphaSlider.svelte.d.ts +9 -0
- package/dist/components/ContrastBadge.svelte +22 -0
- package/dist/components/ContrastBadge.svelte.d.ts +8 -0
- package/dist/components/HsbPicker.svelte +304 -0
- package/dist/components/HsbPicker.svelte.d.ts +9 -0
- package/dist/components/StepIndicator.svelte +87 -0
- package/dist/components/StepIndicator.svelte.d.ts +7 -0
- package/dist/components/TokenPreview.svelte +55 -0
- package/dist/components/TokenPreview.svelte.d.ts +8 -0
- package/dist/components/WizardShell.svelte +140 -0
- package/dist/components/WizardShell.svelte.d.ts +15 -0
- package/dist/engine/derivation.d.ts +282 -0
- package/dist/engine/derivation.js +1445 -0
- package/dist/engine/derivation.test.d.ts +1 -0
- package/dist/engine/derivation.test.js +956 -0
- package/dist/engine/export-css.d.ts +32 -0
- package/dist/engine/export-css.js +90 -0
- package/dist/engine/export-css.test.d.ts +1 -0
- package/dist/engine/export-css.test.js +78 -0
- package/dist/engine/index.d.ts +10 -0
- package/dist/engine/index.js +6 -0
- package/dist/engine/palette.d.ts +16 -0
- package/dist/engine/palette.js +44 -0
- package/dist/engine/presets.d.ts +6 -0
- package/dist/engine/presets.js +34 -0
- package/dist/engine/url-codec.d.ts +53 -0
- package/dist/engine/url-codec.js +243 -0
- package/dist/engine/url-codec.test.d.ts +1 -0
- package/dist/engine/url-codec.test.js +137 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +17 -0
- package/dist/state.svelte.d.ts +104 -0
- package/dist/state.svelte.js +555 -0
- package/dist/steps/BrandColor.svelte +218 -0
- package/dist/steps/BrandColor.svelte.d.ts +6 -0
- package/dist/steps/Personality.svelte +319 -0
- package/dist/steps/Personality.svelte.d.ts +3 -0
- package/dist/steps/PreviewExport.svelte +113 -0
- package/dist/steps/PreviewExport.svelte.d.ts +9 -0
- package/dist/steps/Shape.svelte +121 -0
- package/dist/steps/Shape.svelte.d.ts +18 -0
- package/dist/steps/Typography.svelte +115 -0
- package/dist/steps/Typography.svelte.d.ts +18 -0
- package/package.json +56 -0
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
import { untrack } from 'svelte';
|
|
2
|
+
import { generateTheme } from './engine/derivation.js';
|
|
3
|
+
import { PRESETS } from './engine/presets.js';
|
|
4
|
+
function fmtRem(value) {
|
|
5
|
+
return `${parseFloat(value.toFixed(4))}rem`;
|
|
6
|
+
}
|
|
7
|
+
const STORAGE_KEY = 'dryui-theme-wizard';
|
|
8
|
+
const DEFAULTS = {
|
|
9
|
+
currentStep: 1,
|
|
10
|
+
personality: 'structured',
|
|
11
|
+
brandHsb: { h: 230, s: 65, b: 85 },
|
|
12
|
+
neutralMode: 'monochromatic',
|
|
13
|
+
statusHues: { error: 0, warning: 40, success: 145, info: 210 },
|
|
14
|
+
darkBgOverrides: {},
|
|
15
|
+
fastTrack: false,
|
|
16
|
+
typography: {
|
|
17
|
+
fontPreset: 'System',
|
|
18
|
+
scale: 'default'
|
|
19
|
+
},
|
|
20
|
+
shape: {
|
|
21
|
+
radiusPreset: 'soft',
|
|
22
|
+
radiusScale: 1,
|
|
23
|
+
density: 'default'
|
|
24
|
+
},
|
|
25
|
+
shadows: {
|
|
26
|
+
preset: 'elevated',
|
|
27
|
+
intensity: 1,
|
|
28
|
+
tintBrand: true
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
function loadPersistedState() {
|
|
32
|
+
if (typeof sessionStorage === 'undefined')
|
|
33
|
+
return DEFAULTS;
|
|
34
|
+
try {
|
|
35
|
+
const raw = sessionStorage.getItem(STORAGE_KEY);
|
|
36
|
+
if (!raw)
|
|
37
|
+
return DEFAULTS;
|
|
38
|
+
const saved = JSON.parse(raw);
|
|
39
|
+
return { ...DEFAULTS, ...saved };
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return DEFAULTS;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
let persistTimer;
|
|
46
|
+
function persistState() {
|
|
47
|
+
if (typeof sessionStorage === 'undefined')
|
|
48
|
+
return;
|
|
49
|
+
clearTimeout(persistTimer);
|
|
50
|
+
persistTimer = setTimeout(() => {
|
|
51
|
+
untrack(() => {
|
|
52
|
+
try {
|
|
53
|
+
const { personality, brandHsb, neutralMode, statusHues, darkBgOverrides, typography, shape, shadows } = wizardState;
|
|
54
|
+
sessionStorage.setItem(STORAGE_KEY, JSON.stringify({
|
|
55
|
+
personality,
|
|
56
|
+
brandHsb,
|
|
57
|
+
neutralMode,
|
|
58
|
+
statusHues,
|
|
59
|
+
darkBgOverrides,
|
|
60
|
+
typography,
|
|
61
|
+
shape,
|
|
62
|
+
shadows
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// storage full or unavailable — ignore
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}, 300);
|
|
70
|
+
}
|
|
71
|
+
const initial = loadPersistedState();
|
|
72
|
+
export const wizardState = $state({
|
|
73
|
+
currentStep: initial.currentStep,
|
|
74
|
+
personality: initial.personality,
|
|
75
|
+
brandHsb: initial.brandHsb,
|
|
76
|
+
neutralMode: initial.neutralMode,
|
|
77
|
+
statusHues: initial.statusHues,
|
|
78
|
+
darkBgOverrides: initial.darkBgOverrides,
|
|
79
|
+
fastTrack: initial.fastTrack,
|
|
80
|
+
typography: initial.typography,
|
|
81
|
+
shape: initial.shape,
|
|
82
|
+
shadows: initial.shadows
|
|
83
|
+
});
|
|
84
|
+
// ─── Derived theme ────────────────────────────────────────────────────────────
|
|
85
|
+
const derivedTheme = $derived.by(() => {
|
|
86
|
+
return {
|
|
87
|
+
value: generateTheme(wizardState.brandHsb, {
|
|
88
|
+
neutralMode: wizardState.neutralMode,
|
|
89
|
+
statusHues: wizardState.statusHues,
|
|
90
|
+
darkBg: wizardState.darkBgOverrides
|
|
91
|
+
})
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
export function getDerivedTheme() {
|
|
95
|
+
return derivedTheme.value;
|
|
96
|
+
}
|
|
97
|
+
// ─── Exported functions ───────────────────────────────────────────────────────
|
|
98
|
+
/** Update the brand color (h: 0-360, s/b: 0-100). */
|
|
99
|
+
export function setBrandHsb(h, s, b) {
|
|
100
|
+
wizardState.brandHsb = { h, s, b };
|
|
101
|
+
persistState();
|
|
102
|
+
}
|
|
103
|
+
/** Update a single status tone's hue. */
|
|
104
|
+
export function setStatusHue(tone, hue) {
|
|
105
|
+
wizardState.statusHues[tone] = hue;
|
|
106
|
+
persistState();
|
|
107
|
+
}
|
|
108
|
+
/** Update the neutral palette mode. */
|
|
109
|
+
export function setNeutralMode(mode) {
|
|
110
|
+
wizardState.neutralMode = mode;
|
|
111
|
+
persistState();
|
|
112
|
+
}
|
|
113
|
+
/** Set the personality (chrome level) and apply cross-step defaults. */
|
|
114
|
+
function applyPersonalityDefaults(p) {
|
|
115
|
+
wizardState.personality = p;
|
|
116
|
+
// Cross-step defaults
|
|
117
|
+
const PERSONALITY_SHADOW = {
|
|
118
|
+
minimal: 'flat',
|
|
119
|
+
clean: 'flat',
|
|
120
|
+
structured: 'elevated',
|
|
121
|
+
rich: 'deep'
|
|
122
|
+
};
|
|
123
|
+
const PERSONALITY_RADIUS = {
|
|
124
|
+
minimal: 'soft',
|
|
125
|
+
clean: 'soft',
|
|
126
|
+
structured: 'soft',
|
|
127
|
+
rich: 'rounded'
|
|
128
|
+
};
|
|
129
|
+
wizardState.shadows.preset = PERSONALITY_SHADOW[p];
|
|
130
|
+
wizardState.shape.radiusPreset = PERSONALITY_RADIUS[p];
|
|
131
|
+
}
|
|
132
|
+
/** Set the personality (chrome level) and apply cross-step defaults. */
|
|
133
|
+
export function setPersonality(p) {
|
|
134
|
+
applyPersonalityDefaults(p);
|
|
135
|
+
persistState();
|
|
136
|
+
}
|
|
137
|
+
/** Override a dark background level. */
|
|
138
|
+
export function setDarkBg(level, value) {
|
|
139
|
+
wizardState.darkBgOverrides[level] = value;
|
|
140
|
+
persistState();
|
|
141
|
+
}
|
|
142
|
+
/** Navigate to a specific step (1–5). */
|
|
143
|
+
export function setStep(n) {
|
|
144
|
+
wizardState.currentStep = Math.max(1, Math.min(5, n));
|
|
145
|
+
}
|
|
146
|
+
/** Advance to the next step. */
|
|
147
|
+
export function goNextStep() {
|
|
148
|
+
setStep(wizardState.currentStep + 1);
|
|
149
|
+
}
|
|
150
|
+
/** Go back to the previous step. */
|
|
151
|
+
export function goPrevStep() {
|
|
152
|
+
setStep(wizardState.currentStep - 1);
|
|
153
|
+
}
|
|
154
|
+
/** Enable fast-track mode and jump to the final step. */
|
|
155
|
+
export function activateFastTrack() {
|
|
156
|
+
wizardState.fastTrack = true;
|
|
157
|
+
setStep(5);
|
|
158
|
+
}
|
|
159
|
+
/** Apply a named preset from the PRESETS array. */
|
|
160
|
+
export function applyPreset(name) {
|
|
161
|
+
const preset = PRESETS.find((p) => p.name.toLowerCase() === name.toLowerCase());
|
|
162
|
+
if (!preset) {
|
|
163
|
+
throw new Error(`Unknown preset: "${name}"`);
|
|
164
|
+
}
|
|
165
|
+
wizardState.brandHsb = { ...preset.brandInput };
|
|
166
|
+
persistState();
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Return a CSS custom property string for live preview injection.
|
|
170
|
+
*
|
|
171
|
+
* @param mode - 'light' | 'dark'
|
|
172
|
+
* @returns e.g. `--dry-color-brand: hsl(230, 75%, 60%); ...`
|
|
173
|
+
*/
|
|
174
|
+
export function getStyleString(mode) {
|
|
175
|
+
return Object.entries(getAllTokens(mode))
|
|
176
|
+
.map(([name, value]) => `${name}: ${value}`)
|
|
177
|
+
.join('; ');
|
|
178
|
+
}
|
|
179
|
+
/** Update the font preset name. */
|
|
180
|
+
export function setFontPreset(preset) {
|
|
181
|
+
wizardState.typography.fontPreset = preset;
|
|
182
|
+
persistState();
|
|
183
|
+
}
|
|
184
|
+
/** Update the type scale. */
|
|
185
|
+
export function setTypeScale(scale) {
|
|
186
|
+
wizardState.typography.scale = scale;
|
|
187
|
+
persistState();
|
|
188
|
+
}
|
|
189
|
+
/** Reset all wizard state to defaults. */
|
|
190
|
+
export function resetToDefaults() {
|
|
191
|
+
wizardState.currentStep = 1;
|
|
192
|
+
wizardState.personality = 'structured';
|
|
193
|
+
wizardState.brandHsb = { h: 230, s: 65, b: 85 };
|
|
194
|
+
wizardState.neutralMode = 'monochromatic';
|
|
195
|
+
wizardState.statusHues = { error: 0, warning: 40, success: 145, info: 210 };
|
|
196
|
+
wizardState.darkBgOverrides = {};
|
|
197
|
+
wizardState.fastTrack = false;
|
|
198
|
+
wizardState.typography = { fontPreset: 'System', scale: 'default' };
|
|
199
|
+
wizardState.shape = { radiusPreset: 'soft', radiusScale: 1, density: 'default' };
|
|
200
|
+
wizardState.shadows = { preset: 'elevated', intensity: 1, tintBrand: true };
|
|
201
|
+
if (typeof sessionStorage !== 'undefined') {
|
|
202
|
+
sessionStorage.removeItem(STORAGE_KEY);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/** Apply a full wizard recipe, using personality defaults before explicit overrides. */
|
|
206
|
+
export function applyRecipe(recipe) {
|
|
207
|
+
wizardState.currentStep = DEFAULTS.currentStep;
|
|
208
|
+
wizardState.brandHsb = { ...recipe.brand };
|
|
209
|
+
wizardState.neutralMode = recipe.neutralMode ?? DEFAULTS.neutralMode;
|
|
210
|
+
wizardState.statusHues = { ...DEFAULTS.statusHues, ...recipe.statusHues };
|
|
211
|
+
wizardState.darkBgOverrides = { ...DEFAULTS.darkBgOverrides };
|
|
212
|
+
wizardState.fastTrack = false;
|
|
213
|
+
wizardState.typography = recipe.typography
|
|
214
|
+
? { ...recipe.typography }
|
|
215
|
+
: { ...DEFAULTS.typography };
|
|
216
|
+
wizardState.shape = { ...DEFAULTS.shape };
|
|
217
|
+
wizardState.shadows = { ...DEFAULTS.shadows };
|
|
218
|
+
applyPersonalityDefaults(recipe.personality ?? DEFAULTS.personality);
|
|
219
|
+
if (recipe.shape) {
|
|
220
|
+
wizardState.shape = { ...recipe.shape };
|
|
221
|
+
}
|
|
222
|
+
if (recipe.shadows) {
|
|
223
|
+
wizardState.shadows = { ...recipe.shadows };
|
|
224
|
+
}
|
|
225
|
+
persistState();
|
|
226
|
+
}
|
|
227
|
+
// ─── Shape & spacing ─────────────────────────────────────────────────────────
|
|
228
|
+
export function setRadiusPreset(preset) {
|
|
229
|
+
wizardState.shape.radiusPreset = preset;
|
|
230
|
+
wizardState.shape.radiusScale = 1;
|
|
231
|
+
persistState();
|
|
232
|
+
}
|
|
233
|
+
export function setRadiusScale(scale) {
|
|
234
|
+
wizardState.shape.radiusScale = scale;
|
|
235
|
+
persistState();
|
|
236
|
+
}
|
|
237
|
+
export function setDensity(density) {
|
|
238
|
+
wizardState.shape.density = density;
|
|
239
|
+
persistState();
|
|
240
|
+
}
|
|
241
|
+
export const RADIUS_PRESETS = {
|
|
242
|
+
sharp: { sm: 0, md: 2, lg: 2, xl: 4, '2xl': 4, full: 9999 },
|
|
243
|
+
soft: { sm: 4, md: 8, lg: 8, xl: 12, '2xl': 16, full: 9999 },
|
|
244
|
+
rounded: { sm: 8, md: 12, lg: 16, xl: 20, '2xl': 24, full: 9999 },
|
|
245
|
+
pill: { sm: 9999, md: 9999, lg: 16, xl: 20, '2xl': 24, full: 9999 }
|
|
246
|
+
};
|
|
247
|
+
const DENSITY_FACTORS = {
|
|
248
|
+
compact: 0.85,
|
|
249
|
+
default: 1,
|
|
250
|
+
spacious: 1.15
|
|
251
|
+
};
|
|
252
|
+
// ─── Shadows & elevation ────────────────────────────────────────────────────
|
|
253
|
+
export function setShadowPreset(preset) {
|
|
254
|
+
wizardState.shadows.preset = preset;
|
|
255
|
+
persistState();
|
|
256
|
+
}
|
|
257
|
+
export function setShadowIntensity(intensity) {
|
|
258
|
+
wizardState.shadows.intensity = intensity;
|
|
259
|
+
persistState();
|
|
260
|
+
}
|
|
261
|
+
export function setShadowTint(tint) {
|
|
262
|
+
wizardState.shadows.tintBrand = tint;
|
|
263
|
+
persistState();
|
|
264
|
+
}
|
|
265
|
+
const SHADOW_DEFS = {
|
|
266
|
+
flat: {
|
|
267
|
+
raised: [],
|
|
268
|
+
overlay: []
|
|
269
|
+
},
|
|
270
|
+
subtle: {
|
|
271
|
+
raised: [
|
|
272
|
+
{ y: 1, blur: 3, lightA: 0.06, darkA: 0.5 },
|
|
273
|
+
{ y: 1, blur: 2, lightA: 0.04, darkA: 0.35 }
|
|
274
|
+
],
|
|
275
|
+
overlay: [
|
|
276
|
+
{ y: 4, blur: 16, lightA: 0.1, darkA: 0.6 },
|
|
277
|
+
{ y: 2, blur: 6, lightA: 0.06, darkA: 0.4 }
|
|
278
|
+
]
|
|
279
|
+
},
|
|
280
|
+
elevated: {
|
|
281
|
+
raised: [
|
|
282
|
+
{ y: 2, blur: 6, lightA: 0.1, darkA: 0.65 },
|
|
283
|
+
{ y: 1, blur: 3, lightA: 0.07, darkA: 0.45 }
|
|
284
|
+
],
|
|
285
|
+
overlay: [
|
|
286
|
+
{ y: 8, blur: 28, lightA: 0.14, darkA: 0.7 },
|
|
287
|
+
{ y: 3, blur: 10, lightA: 0.1, darkA: 0.5 }
|
|
288
|
+
]
|
|
289
|
+
},
|
|
290
|
+
deep: {
|
|
291
|
+
raised: [
|
|
292
|
+
{ y: 4, blur: 14, lightA: 0.16, darkA: 0.8 },
|
|
293
|
+
{ y: 2, blur: 5, lightA: 0.1, darkA: 0.55 }
|
|
294
|
+
],
|
|
295
|
+
overlay: [
|
|
296
|
+
{ y: 16, blur: 48, lightA: 0.22, darkA: 0.85 },
|
|
297
|
+
{ y: 6, blur: 18, lightA: 0.14, darkA: 0.6 }
|
|
298
|
+
]
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
function buildShadowValue(layers, mode, hue, sat, lightness, intensity) {
|
|
302
|
+
if (layers.length === 0)
|
|
303
|
+
return 'none';
|
|
304
|
+
return layers
|
|
305
|
+
.map((l) => {
|
|
306
|
+
const a = (mode === 'light' ? l.lightA : l.darkA) * intensity;
|
|
307
|
+
return `0 ${l.y}px ${l.blur}px hsla(${hue}, ${sat}%, ${lightness}%, ${Math.min(a, 1).toFixed(3)})`;
|
|
308
|
+
})
|
|
309
|
+
.join(', ');
|
|
310
|
+
}
|
|
311
|
+
export function getShadowTokens(shadowPreset = wizardState.shadows.preset, shadowIntensity = wizardState.shadows.intensity, tintBrand = wizardState.shadows.tintBrand, brandHue = wizardState.brandHsb.h) {
|
|
312
|
+
const H = Math.round(brandHue);
|
|
313
|
+
const hue = tintBrand ? H : 0;
|
|
314
|
+
const def = SHADOW_DEFS[shadowPreset];
|
|
315
|
+
return {
|
|
316
|
+
light: {
|
|
317
|
+
'--dry-shadow-raised': buildShadowValue(def.raised, 'light', hue, 20, 20, shadowIntensity),
|
|
318
|
+
'--dry-shadow-overlay': buildShadowValue(def.overlay, 'light', hue, 20, 20, shadowIntensity)
|
|
319
|
+
},
|
|
320
|
+
dark: {
|
|
321
|
+
'--dry-shadow-raised': buildShadowValue(def.raised, 'dark', hue, 15, 2, shadowIntensity),
|
|
322
|
+
'--dry-shadow-overlay': buildShadowValue(def.overlay, 'dark', hue, 15, 2, shadowIntensity)
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
const SPACE_SCALE = [
|
|
327
|
+
['0_5', 0.125],
|
|
328
|
+
['1', 0.25],
|
|
329
|
+
['1_5', 0.375],
|
|
330
|
+
['2', 0.5],
|
|
331
|
+
['2_5', 0.625],
|
|
332
|
+
['3', 0.75],
|
|
333
|
+
['3_5', 0.875],
|
|
334
|
+
['4', 1],
|
|
335
|
+
['5', 1.25],
|
|
336
|
+
['6', 1.5],
|
|
337
|
+
['7', 1.75],
|
|
338
|
+
['8', 2],
|
|
339
|
+
['9', 2.25],
|
|
340
|
+
['10', 2.5],
|
|
341
|
+
['11', 2.75],
|
|
342
|
+
['12', 3],
|
|
343
|
+
['14', 3.5],
|
|
344
|
+
['16', 4],
|
|
345
|
+
['20', 5],
|
|
346
|
+
['24', 6],
|
|
347
|
+
['32', 8]
|
|
348
|
+
];
|
|
349
|
+
export function getShapeTokens(radiusPreset = wizardState.shape.radiusPreset, radiusScale = wizardState.shape.radiusScale, densityPreset = wizardState.shape.density) {
|
|
350
|
+
const preset = RADIUS_PRESETS[radiusPreset];
|
|
351
|
+
const scale = radiusScale;
|
|
352
|
+
const density = DENSITY_FACTORS[densityPreset];
|
|
353
|
+
const tokens = {};
|
|
354
|
+
for (const [key, base] of Object.entries(preset)) {
|
|
355
|
+
let px;
|
|
356
|
+
if (base >= 9999) {
|
|
357
|
+
px = scale >= 1 ? 9999 : Math.round(24 * scale);
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
px = Math.round(base * scale);
|
|
361
|
+
}
|
|
362
|
+
tokens[`--dry-radius-${key}`] = `${px}px`;
|
|
363
|
+
}
|
|
364
|
+
for (const [key, rem] of SPACE_SCALE) {
|
|
365
|
+
tokens[`--dry-space-${key}`] = fmtRem(rem * density);
|
|
366
|
+
}
|
|
367
|
+
return tokens;
|
|
368
|
+
}
|
|
369
|
+
// ─── Personality / chrome ───────────────────────────────────────────────────
|
|
370
|
+
const PERSONALITY_TOKENS = {
|
|
371
|
+
minimal: {
|
|
372
|
+
// Surface role
|
|
373
|
+
'--dry-surface-bg': 'transparent',
|
|
374
|
+
'--dry-surface-border': 'transparent',
|
|
375
|
+
'--dry-surface-shadow': 'none',
|
|
376
|
+
'--dry-surface-radius': '0',
|
|
377
|
+
'--dry-surface-padding': 'var(--dry-space-4)',
|
|
378
|
+
// Chrome role
|
|
379
|
+
'--dry-chrome-bg': 'transparent',
|
|
380
|
+
'--dry-chrome-border': 'transparent',
|
|
381
|
+
'--dry-chrome-shadow': 'none',
|
|
382
|
+
// Overlay role
|
|
383
|
+
'--dry-overlay-bg': 'var(--dry-color-bg-overlay)',
|
|
384
|
+
'--dry-overlay-border': 'var(--dry-color-stroke-weak)',
|
|
385
|
+
'--dry-overlay-shadow': 'var(--dry-shadow-sm)',
|
|
386
|
+
'--dry-overlay-radius': 'var(--dry-radius-lg)',
|
|
387
|
+
// Control role
|
|
388
|
+
'--dry-control-bg': 'transparent',
|
|
389
|
+
'--dry-control-border': 'var(--dry-color-stroke-weak)',
|
|
390
|
+
'--dry-control-radius': 'var(--dry-radius-sm)',
|
|
391
|
+
// Page role
|
|
392
|
+
'--dry-page-bg': 'transparent',
|
|
393
|
+
'--dry-page-border': 'transparent',
|
|
394
|
+
'--dry-page-shadow': 'none',
|
|
395
|
+
'--dry-page-radius': '0'
|
|
396
|
+
},
|
|
397
|
+
clean: {
|
|
398
|
+
// Surface role
|
|
399
|
+
'--dry-surface-bg': 'var(--dry-color-bg-raised)',
|
|
400
|
+
'--dry-surface-border': 'transparent',
|
|
401
|
+
'--dry-surface-shadow': 'none',
|
|
402
|
+
'--dry-surface-radius': 'var(--dry-radius-lg)',
|
|
403
|
+
'--dry-surface-padding': 'var(--dry-space-6)',
|
|
404
|
+
// Chrome role
|
|
405
|
+
'--dry-chrome-bg': 'transparent',
|
|
406
|
+
'--dry-chrome-border': 'var(--dry-color-stroke-weak)',
|
|
407
|
+
'--dry-chrome-shadow': 'none',
|
|
408
|
+
// Overlay role
|
|
409
|
+
'--dry-overlay-bg': 'var(--dry-color-bg-overlay)',
|
|
410
|
+
'--dry-overlay-border': 'var(--dry-color-stroke-weak)',
|
|
411
|
+
'--dry-overlay-shadow': 'var(--dry-shadow-md)',
|
|
412
|
+
'--dry-overlay-radius': 'var(--dry-radius-lg)',
|
|
413
|
+
// Control role
|
|
414
|
+
'--dry-control-bg': 'var(--dry-color-bg-raised)',
|
|
415
|
+
'--dry-control-border': 'var(--dry-color-stroke-strong)',
|
|
416
|
+
'--dry-control-radius': 'var(--dry-radius-md)',
|
|
417
|
+
// Page role
|
|
418
|
+
'--dry-page-bg': 'transparent',
|
|
419
|
+
'--dry-page-border': 'transparent',
|
|
420
|
+
'--dry-page-shadow': 'none',
|
|
421
|
+
'--dry-page-radius': 'var(--dry-radius-lg)'
|
|
422
|
+
},
|
|
423
|
+
structured: {
|
|
424
|
+
// Surface role
|
|
425
|
+
'--dry-surface-bg': 'var(--dry-color-bg-raised)',
|
|
426
|
+
'--dry-surface-border': 'var(--dry-color-stroke-weak)',
|
|
427
|
+
'--dry-surface-shadow': 'var(--dry-shadow-raised)',
|
|
428
|
+
'--dry-surface-radius': 'var(--dry-radius-xl)',
|
|
429
|
+
'--dry-surface-padding': 'var(--dry-space-8)',
|
|
430
|
+
// Chrome role
|
|
431
|
+
'--dry-chrome-bg': 'var(--dry-color-bg-raised)',
|
|
432
|
+
'--dry-chrome-border': 'var(--dry-color-stroke-weak)',
|
|
433
|
+
'--dry-chrome-shadow': 'var(--dry-shadow-sm)',
|
|
434
|
+
// Overlay role
|
|
435
|
+
'--dry-overlay-bg': 'var(--dry-color-bg-overlay)',
|
|
436
|
+
'--dry-overlay-border': 'var(--dry-color-stroke-weak)',
|
|
437
|
+
'--dry-overlay-shadow': 'var(--dry-shadow-lg)',
|
|
438
|
+
'--dry-overlay-radius': 'var(--dry-radius-xl)',
|
|
439
|
+
// Control role
|
|
440
|
+
'--dry-control-bg': 'var(--dry-color-bg-raised)',
|
|
441
|
+
'--dry-control-border': 'var(--dry-color-stroke-strong)',
|
|
442
|
+
'--dry-control-radius': 'var(--dry-radius-md)',
|
|
443
|
+
// Page role
|
|
444
|
+
'--dry-page-bg': 'var(--dry-color-bg-overlay)',
|
|
445
|
+
'--dry-page-border': 'var(--dry-color-stroke-weak)',
|
|
446
|
+
'--dry-page-shadow': 'var(--dry-shadow-sm)',
|
|
447
|
+
'--dry-page-radius': 'var(--dry-radius-xl)'
|
|
448
|
+
},
|
|
449
|
+
rich: {
|
|
450
|
+
// Surface role
|
|
451
|
+
'--dry-surface-bg': 'var(--dry-color-bg-overlay)',
|
|
452
|
+
'--dry-surface-border': 'var(--dry-color-stroke-weak)',
|
|
453
|
+
'--dry-surface-shadow': 'var(--dry-shadow-overlay)',
|
|
454
|
+
'--dry-surface-radius': 'var(--dry-radius-2xl)',
|
|
455
|
+
'--dry-surface-padding': 'var(--dry-space-10)',
|
|
456
|
+
// Chrome role
|
|
457
|
+
'--dry-chrome-bg': 'var(--dry-color-bg-overlay)',
|
|
458
|
+
'--dry-chrome-border': 'var(--dry-color-stroke-weak)',
|
|
459
|
+
'--dry-chrome-shadow': 'var(--dry-shadow-overlay)',
|
|
460
|
+
// Overlay role
|
|
461
|
+
'--dry-overlay-bg': 'var(--dry-color-bg-overlay)',
|
|
462
|
+
'--dry-overlay-border': 'var(--dry-color-stroke-weak)',
|
|
463
|
+
'--dry-overlay-shadow': 'var(--dry-shadow-overlay)',
|
|
464
|
+
'--dry-overlay-radius': 'var(--dry-radius-2xl)',
|
|
465
|
+
// Control role
|
|
466
|
+
'--dry-control-bg': 'var(--dry-color-bg-raised)',
|
|
467
|
+
'--dry-control-border': 'var(--dry-color-stroke-strong)',
|
|
468
|
+
'--dry-control-radius': 'var(--dry-radius-lg)',
|
|
469
|
+
// Page role
|
|
470
|
+
'--dry-page-bg': 'var(--dry-color-bg-raised)',
|
|
471
|
+
'--dry-page-border': 'var(--dry-color-stroke-weak)',
|
|
472
|
+
'--dry-page-shadow': 'var(--dry-shadow-overlay)',
|
|
473
|
+
'--dry-page-radius': 'var(--dry-radius-2xl)'
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
export function getPersonalityTokens() {
|
|
477
|
+
return PERSONALITY_TOKENS[wizardState.personality];
|
|
478
|
+
}
|
|
479
|
+
// ─── Typography tokens ──────────────────────────────────────────────────────
|
|
480
|
+
export const FONT_STACKS = {
|
|
481
|
+
System: 'ui-sans-serif, system-ui, -apple-system, sans-serif',
|
|
482
|
+
Humanist: 'Seravek, "Gill Sans Nova", Ubuntu, Calibri, "DejaVu Sans", sans-serif',
|
|
483
|
+
Geometric: 'Avenir, Montserrat, Corbel, "URW Gothic", source-sans-pro, sans-serif',
|
|
484
|
+
Classical: 'Optima, Candara, "Noto Sans", source-sans-pro, sans-serif',
|
|
485
|
+
Serif: 'Charter, "Bitstream Charter", "Sitka Text", Cambria, serif',
|
|
486
|
+
Mono: 'ui-monospace, "SF Mono", Menlo, Consolas, monospace'
|
|
487
|
+
};
|
|
488
|
+
const TYPE_BASE = {
|
|
489
|
+
display: { size: 3.5, leading: 4 },
|
|
490
|
+
'heading-1': { size: 2.5, leading: 3 },
|
|
491
|
+
'heading-2': { size: 2, leading: 2.5 },
|
|
492
|
+
'heading-3': { size: 1.5, leading: 2 },
|
|
493
|
+
'heading-4': { size: 1.25, leading: 1.75 },
|
|
494
|
+
small: { size: 1, leading: 1.5 },
|
|
495
|
+
tiny: { size: 0.875, leading: 1.25 }
|
|
496
|
+
};
|
|
497
|
+
const TYPE_SCALE_FACTORS = {
|
|
498
|
+
compact: 0.9,
|
|
499
|
+
default: 1,
|
|
500
|
+
spacious: 1.1
|
|
501
|
+
};
|
|
502
|
+
export function getTypographyTokens(fontPreset = wizardState.typography.fontPreset, scale = wizardState.typography.scale) {
|
|
503
|
+
const factor = TYPE_SCALE_FACTORS[scale];
|
|
504
|
+
const tokens = {
|
|
505
|
+
'--dry-font-sans': FONT_STACKS[fontPreset]
|
|
506
|
+
};
|
|
507
|
+
for (const [name, { size, leading }] of Object.entries(TYPE_BASE)) {
|
|
508
|
+
tokens[`--dry-type-${name}-size`] = fmtRem(size * factor);
|
|
509
|
+
tokens[`--dry-type-${name}-leading`] = fmtRem(leading * factor);
|
|
510
|
+
}
|
|
511
|
+
return tokens;
|
|
512
|
+
}
|
|
513
|
+
/** Return all tokens (color + shape + shadow + personality + typography) merged for a given mode. */
|
|
514
|
+
export function getAllTokens(mode = 'light') {
|
|
515
|
+
const m = mode;
|
|
516
|
+
const theme = derivedTheme.value;
|
|
517
|
+
const colorTokens = m === 'dark' ? theme.dark : theme.light;
|
|
518
|
+
const shapeTokens = getShapeTokens();
|
|
519
|
+
const shadows = getShadowTokens();
|
|
520
|
+
const shadowTokens = shadows[m];
|
|
521
|
+
const personalityTokens = getPersonalityTokens();
|
|
522
|
+
const typographyTokens = getTypographyTokens();
|
|
523
|
+
return { ...colorTokens, ...shapeTokens, ...shadowTokens, ...personalityTokens, ...typographyTokens };
|
|
524
|
+
}
|
|
525
|
+
/** Return only tokens that the user has changed from defaults. */
|
|
526
|
+
export function getOverrideTokens(mode = 'light') {
|
|
527
|
+
const all = getAllTokens(mode);
|
|
528
|
+
const m = mode;
|
|
529
|
+
const base = getDefaultAllTokens(m);
|
|
530
|
+
const overrides = {};
|
|
531
|
+
for (const k in all) {
|
|
532
|
+
if (all[k] !== base[k]) {
|
|
533
|
+
overrides[k] = all[k];
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
return overrides;
|
|
537
|
+
}
|
|
538
|
+
/** getAllTokens computed with DEFAULTS — cached per mode. */
|
|
539
|
+
function getDefaultAllTokens(mode) {
|
|
540
|
+
return (defaultAllTokensCache[mode] ??= computeDefaultAllTokens(mode));
|
|
541
|
+
}
|
|
542
|
+
const defaultAllTokensCache = {};
|
|
543
|
+
function computeDefaultAllTokens(mode) {
|
|
544
|
+
const theme = generateTheme(DEFAULTS.brandHsb, {
|
|
545
|
+
neutralMode: DEFAULTS.neutralMode,
|
|
546
|
+
statusHues: DEFAULTS.statusHues,
|
|
547
|
+
darkBg: DEFAULTS.darkBgOverrides
|
|
548
|
+
});
|
|
549
|
+
const colorTokens = mode === 'dark' ? theme.dark : theme.light;
|
|
550
|
+
const shapeTokens = getShapeTokens(DEFAULTS.shape.radiusPreset, DEFAULTS.shape.radiusScale, DEFAULTS.shape.density);
|
|
551
|
+
const shadowTokens = getShadowTokens(DEFAULTS.shadows.preset, DEFAULTS.shadows.intensity, DEFAULTS.shadows.tintBrand, DEFAULTS.brandHsb.h)[mode];
|
|
552
|
+
const personalityTokens = PERSONALITY_TOKENS[DEFAULTS.personality];
|
|
553
|
+
const typographyTokens = getTypographyTokens(DEFAULTS.typography.fontPreset, DEFAULTS.typography.scale);
|
|
554
|
+
return { ...colorTokens, ...shapeTokens, ...shadowTokens, ...personalityTokens, ...typographyTokens };
|
|
555
|
+
}
|