@motion-proto/live-tokens 0.9.0 → 0.11.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/README.md +50 -29
- package/dist-plugin/index.cjs +177 -125
- package/dist-plugin/index.d.cts +3 -2
- package/dist-plugin/index.d.ts +3 -2
- package/dist-plugin/index.js +177 -125
- package/package.json +4 -1
- package/src/editor/component-editor/BadgeEditor.svelte +44 -42
- package/src/editor/component-editor/ButtonEditor.svelte +224 -0
- package/src/editor/component-editor/CardEditor.svelte +2 -0
- package/src/editor/component-editor/CollapsibleSectionEditor.svelte +1 -7
- package/src/editor/component-editor/CornerBadgeEditor.svelte +44 -34
- package/src/editor/component-editor/ImageLightboxEditor.svelte +58 -0
- package/src/editor/component-editor/InputEditor.svelte +272 -0
- package/src/editor/component-editor/NotificationEditor.svelte +44 -65
- package/src/editor/component-editor/ProgressBarEditor.svelte +71 -87
- package/src/editor/component-editor/SegmentedControlEditor.svelte +98 -37
- package/src/editor/component-editor/SideNavigationEditor.svelte +342 -0
- package/src/editor/component-editor/registry.ts +35 -2
- package/src/editor/component-editor/scaffolding/ComponentFileManager.svelte +3 -2
- package/src/editor/component-editor/scaffolding/ShadowBackdrop.svelte +10 -1
- package/src/editor/component-editor/scaffolding/StateBlock.svelte +9 -10
- package/src/editor/component-editor/scaffolding/TokenLayout.svelte +60 -36
- package/src/editor/component-editor/scaffolding/VariantGroup.svelte +38 -1
- package/src/editor/component-editor/scaffolding/buildTypeGroupTokens.ts +1 -1
- package/src/editor/component-editor/scaffolding/siblings.ts +2 -2
- package/src/editor/component-editor/scaffolding/types.ts +2 -1
- package/src/editor/core/components/componentConfigService.ts +7 -6
- package/src/editor/core/manifests/manifestService.ts +5 -4
- package/src/editor/core/storage/apiBase.ts +15 -0
- package/src/editor/core/storage/files/versionedFileResourceClient.ts +1 -1
- package/src/editor/core/store/editorRenderer.ts +1 -4
- package/src/editor/core/store/editorStore.ts +5 -9
- package/src/editor/core/store/editorTypes.ts +6 -13
- package/src/editor/core/themes/migrations/2026-05-13-primary-to-brand.ts +2 -29
- package/src/editor/core/themes/migrations/2026-05-24-collapsiblesection-drop-active-state.ts +28 -0
- package/src/editor/core/themes/migrations/2026-05-24-progressbar-collapse-variants.ts +41 -0
- package/src/editor/core/themes/migrations/2026-05-24-promote-state-shared-tokens.ts +59 -0
- package/src/editor/core/themes/migrations/2026-05-24-segmentedcontrol-divider-inset.ts +29 -0
- package/src/editor/core/themes/migrations/2026-05-25-cornerbadge-flatten-variants.ts +46 -0
- package/src/editor/core/themes/migrations/2026-05-26-drop-overlay-extra-stops.ts +46 -0
- package/src/editor/core/themes/migrations/index.ts +16 -0
- package/src/editor/core/themes/slices/overlays.ts +44 -75
- package/src/editor/core/themes/themeInit.ts +3 -2
- package/src/editor/core/themes/themeService.ts +3 -2
- package/src/editor/ui/SurfacesTab.svelte +3 -7
- package/src/editor/ui/UIEasingSelector.svelte +240 -0
- package/src/editor/ui/UIPaddingSelector.svelte +2 -2
- package/src/editor/ui/UIPaletteSelector.svelte +151 -36
- package/src/editor/ui/{UIRelinkConfirmPopover.svelte → UIRelinkConfirmDialog.svelte} +107 -75
- package/src/editor/ui/UITokenSelector.svelte +15 -2
- package/src/editor/ui/sections/OverlaysSection.svelte +107 -540
- package/src/editor/ui/variantScales.ts +34 -0
- package/src/system/components/Button.svelte +34 -85
- package/src/system/components/Card.svelte +2 -1
- package/src/system/components/CollapsibleSection.svelte +1 -48
- package/src/system/components/CornerBadge.svelte +72 -138
- package/src/system/components/ImageLightbox.svelte +578 -0
- package/src/system/components/Input.svelte +387 -0
- package/src/system/components/ProgressBar.svelte +62 -258
- package/src/system/components/SegmentedControl.svelte +81 -15
- package/src/system/components/SideNavigation.svelte +777 -0
- package/src/system/components/TabBar.svelte +1 -1
- package/src/system/styles/tokens.css +48 -5
- package/src/system/styles/tokens.generated.css +33 -185
- package/src/editor/component-editor/StandardButtonsEditor.svelte +0 -190
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
/**
|
|
3
|
-
* Overlay
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
3
|
+
* Overlay + hover stops. Each stop binds an existing color token through
|
|
4
|
+
* `UIPaletteSelector`; the picker handles family/step selection and
|
|
5
|
+
* opacity, and writes route through `onwrite` into the overlays slice
|
|
6
|
+
* (see editorStore.makeDefaultOverlaysState). The slice fans the
|
|
7
|
+
* resulting color-mix expressions out to :root.
|
|
8
8
|
*/
|
|
9
|
-
import { editorState, mutate
|
|
10
|
-
import type { OverlayToken
|
|
9
|
+
import { editorState, mutate } from '../../core/store/editorStore';
|
|
10
|
+
import type { OverlayToken } from '../../core/store/editorTypes';
|
|
11
|
+
import {
|
|
12
|
+
makeDefaultOverlayTokens,
|
|
13
|
+
makeDefaultHoverTokens,
|
|
14
|
+
parseOverlayCss,
|
|
15
|
+
} from '../../core/themes/slices/overlays';
|
|
16
|
+
import UIPaletteSelector from '../UIPaletteSelector.svelte';
|
|
11
17
|
|
|
12
18
|
interface Props {
|
|
13
19
|
copiedVar?: string | null;
|
|
@@ -16,96 +22,36 @@
|
|
|
16
22
|
|
|
17
23
|
let { copiedVar = null, oncopy }: Props = $props();
|
|
18
24
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
let editingOverlay: string | null = $state(null);
|
|
22
|
-
|
|
23
|
-
function clampNum(v: number, lo: number, hi: number): number {
|
|
24
|
-
return Math.max(lo, Math.min(hi, Math.round(v)));
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function getOverlayCss(t: OverlayToken): string {
|
|
28
|
-
return `rgba(${t.r}, ${t.g}, ${t.b}, ${t.opacity})`;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function hslToRgb(h: number, s: number, l: number): { r: number; g: number; b: number } {
|
|
32
|
-
s /= 100; l /= 100;
|
|
33
|
-
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
34
|
-
const x = c * (1 - Math.abs((h / 60) % 2 - 1));
|
|
35
|
-
const m = l - c / 2;
|
|
36
|
-
let r1 = 0, g1 = 0, b1 = 0;
|
|
37
|
-
if (h < 60) { r1 = c; g1 = x; }
|
|
38
|
-
else if (h < 120) { r1 = x; g1 = c; }
|
|
39
|
-
else if (h < 180) { g1 = c; b1 = x; }
|
|
40
|
-
else if (h < 240) { g1 = x; b1 = c; }
|
|
41
|
-
else if (h < 300) { r1 = x; b1 = c; }
|
|
42
|
-
else { r1 = c; b1 = x; }
|
|
43
|
-
return {
|
|
44
|
-
r: Math.round((r1 + m) * 255),
|
|
45
|
-
g: Math.round((g1 + m) * 255),
|
|
46
|
-
b: Math.round((b1 + m) * 255),
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function applyChannelColor(tokens: OverlayToken[], g: OverlayChannelGlobals): void {
|
|
51
|
-
const rgb = hslToRgb(g.hue, g.saturation, g.lightness);
|
|
52
|
-
for (const t of tokens) { t.r = rgb.r; t.g = rgb.g; t.b = rgb.b; }
|
|
53
|
-
}
|
|
54
|
-
function applyChannelOpacity(tokens: OverlayToken[], g: OverlayChannelGlobals): void {
|
|
55
|
-
const last = tokens.length - 1;
|
|
56
|
-
tokens.forEach((t, i) => {
|
|
57
|
-
const frac = last > 0 ? i / last : 0.5;
|
|
58
|
-
t.opacity = Math.round((g.opacityMin + frac * (g.opacityMax - g.opacityMin)) * 100) / 100;
|
|
59
|
-
});
|
|
60
|
-
}
|
|
25
|
+
type Channel = 'overlay' | 'hover';
|
|
61
26
|
|
|
62
|
-
|
|
63
|
-
type ColorField = 'hue' | 'saturation' | 'lightness';
|
|
64
|
-
type OpacityField = 'opacityMin' | 'opacityMax';
|
|
65
|
-
|
|
66
|
-
function pickChannelTokens(s: EditorState, ch: OverlayChannel): OverlayToken[] {
|
|
67
|
-
return ch === 'overlay' ? s.overlays.tokens : s.overlays.hoverTokens;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function setOverlayColor(ch: OverlayChannel, field: ColorField, value: number) {
|
|
71
|
-
mutate(`${ch} ${field}`, (s) => {
|
|
72
|
-
const g = s.overlays.globals[ch];
|
|
73
|
-
if (field === 'hue') g.hue = clampNum(value, 0, 360);
|
|
74
|
-
else if (field === 'saturation') g.saturation = clampNum(value, 0, 100);
|
|
75
|
-
else g.lightness = clampNum(value, 0, 100);
|
|
76
|
-
applyChannelColor(pickChannelTokens(s, ch), g);
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function setOverlayOpacity(ch: OverlayChannel, field: OpacityField, value01: number) {
|
|
81
|
-
mutate(`${ch} ${field}`, (s) => {
|
|
82
|
-
const g = s.overlays.globals[ch];
|
|
83
|
-
const clamped = Math.max(0, Math.min(1, value01));
|
|
84
|
-
if (field === 'opacityMin') g.opacityMin = clamped;
|
|
85
|
-
else g.opacityMax = clamped;
|
|
86
|
-
applyChannelOpacity(pickChannelTokens(s, ch), g);
|
|
87
|
-
});
|
|
88
|
-
}
|
|
27
|
+
function copy(v: string) { oncopy?.(v); }
|
|
89
28
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
29
|
+
/** Translate a UITokenSelector write payload into a slice mutation.
|
|
30
|
+
* - `null` → restore this stop's default alias + opacity (reset).
|
|
31
|
+
* - bare `--name` → 100% opacity on that alias.
|
|
32
|
+
* - `color-mix(in srgb, var(--name) N%, transparent)` → alias + N%.
|
|
33
|
+
* - other (incl. `transparent`) → ignored; the picker's "None" option
|
|
34
|
+
* doesn't apply to overlays. */
|
|
35
|
+
function handleWrite(channel: Channel, idx: number, value: string | null) {
|
|
36
|
+
mutate(`${channel} ${idx} edit`, (s) => {
|
|
37
|
+
const arr = channel === 'overlay' ? s.overlays.tokens : s.overlays.hoverTokens;
|
|
93
38
|
const t = arr[idx];
|
|
94
39
|
if (!t) return;
|
|
95
|
-
|
|
40
|
+
if (value === null) {
|
|
41
|
+
const defaults = channel === 'overlay' ? makeDefaultOverlayTokens() : makeDefaultHoverTokens();
|
|
42
|
+
const d = defaults[idx];
|
|
43
|
+
if (d) { t.alias = d.alias; t.opacity = d.opacity; }
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (value.startsWith('--')) { t.alias = value; t.opacity = 1; return; }
|
|
47
|
+
const parsed = parseOverlayCss(value);
|
|
48
|
+
if (parsed) { t.alias = parsed.alias; t.opacity = parsed.opacity; }
|
|
96
49
|
});
|
|
97
50
|
}
|
|
98
51
|
|
|
99
|
-
function
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
})`;
|
|
103
|
-
}
|
|
104
|
-
function overlaySatGrad(g: OverlayChannelGlobals): string {
|
|
105
|
-
return `linear-gradient(to right, hsl(${g.hue},0%,${g.lightness}%), hsl(${g.hue},100%,${g.lightness}%))`;
|
|
106
|
-
}
|
|
107
|
-
function overlayLightGrad(g: OverlayChannelGlobals): string {
|
|
108
|
-
return `linear-gradient(to right, hsl(${g.hue},${g.saturation}%,0%), hsl(${g.hue},${g.saturation}%,50%), hsl(${g.hue},${g.saturation}%,100%))`;
|
|
52
|
+
function copyCss(t: OverlayToken): string {
|
|
53
|
+
const pct = Math.round(t.opacity * 100);
|
|
54
|
+
return pct >= 100 ? `var(${t.alias})` : `color-mix(in srgb, var(${t.alias}) ${pct}%, transparent)`;
|
|
109
55
|
}
|
|
110
56
|
</script>
|
|
111
57
|
|
|
@@ -114,223 +60,69 @@
|
|
|
114
60
|
|
|
115
61
|
<h3 class="group-title">Dark Overlays</h3>
|
|
116
62
|
<div class="overlays-grid">
|
|
117
|
-
{#each $editorState.overlays.tokens as token, i}
|
|
118
|
-
<div class="overlay-
|
|
63
|
+
{#each $editorState.overlays.tokens as token, i (token.variable)}
|
|
64
|
+
<div class="overlay-row">
|
|
119
65
|
<div class="overlay-swatch-wrap">
|
|
120
|
-
<div class="overlay-swatch" style="background: {
|
|
66
|
+
<div class="overlay-swatch" style="background: var({token.variable});"></div>
|
|
121
67
|
</div>
|
|
122
|
-
<div class="
|
|
123
|
-
<button
|
|
68
|
+
<div class="overlay-meta">
|
|
69
|
+
<button
|
|
70
|
+
class="token-variable copyable"
|
|
71
|
+
class:copied={copiedVar === token.variable}
|
|
72
|
+
onclick={() => copy(token.variable)}
|
|
73
|
+
>{copiedVar === token.variable ? 'copied!' : token.variable}</button>
|
|
124
74
|
<span class="token-value">{token.label} — {Math.round(token.opacity * 100)}%</span>
|
|
125
75
|
</div>
|
|
126
|
-
<
|
|
127
|
-
|
|
76
|
+
<div class="overlay-picker">
|
|
77
|
+
<UIPaletteSelector
|
|
78
|
+
variable={token.variable}
|
|
79
|
+
showNone={false}
|
|
80
|
+
onwrite={(v) => handleWrite('overlay', i, v)}
|
|
81
|
+
/>
|
|
82
|
+
</div>
|
|
83
|
+
<button
|
|
84
|
+
class="copy-css-btn"
|
|
85
|
+
title="Copy resolved CSS"
|
|
86
|
+
onclick={() => copy(copyCss(token))}
|
|
87
|
+
>
|
|
88
|
+
{copiedVar === copyCss(token) ? 'copied!' : 'copy css'}
|
|
128
89
|
</button>
|
|
129
|
-
{#if editingOverlay === token.variable}
|
|
130
|
-
<div class="shadow-editor">
|
|
131
|
-
<div class="shadow-slider-row">
|
|
132
|
-
<span class="shadow-slider-label">Opacity</span>
|
|
133
|
-
<input type="range" min="0" max="100" value={Math.round(token.opacity * 100)}
|
|
134
|
-
onpointerdown={() => beginSliderGesture(`edit ${token.variable} opacity`)}
|
|
135
|
-
oninput={(e) => setOverlayTokenOpacity('overlay', i, +e.currentTarget.value / 100)} />
|
|
136
|
-
<input class="shadow-slider-input" type="number" min="0" max="100"
|
|
137
|
-
value={Math.round(token.opacity * 100)}
|
|
138
|
-
onchange={(e) => setOverlayTokenOpacity('overlay', i, Math.min(100, Math.max(0, +e.currentTarget.value)) / 100)} />
|
|
139
|
-
<span class="shadow-slider-unit">%</span>
|
|
140
|
-
</div>
|
|
141
|
-
<div class="shadow-css-output">
|
|
142
|
-
<code>{getOverlayCss(token)}</code>
|
|
143
|
-
<button class="shadow-copy-btn" onclick={() => copy(getOverlayCss(token))}>
|
|
144
|
-
{copiedVar === getOverlayCss(token) ? 'Copied!' : 'Copy CSS'}
|
|
145
|
-
</button>
|
|
146
|
-
</div>
|
|
147
|
-
</div>
|
|
148
|
-
{/if}
|
|
149
90
|
</div>
|
|
150
91
|
{/each}
|
|
151
92
|
</div>
|
|
152
93
|
|
|
153
|
-
|
|
154
|
-
<div class="overlay-global-editor">
|
|
155
|
-
<h4 class="global-shadow-title">Global Overlay Controls</h4>
|
|
156
|
-
<div class="overlay-global-columns">
|
|
157
|
-
<div class="overlay-global-col">
|
|
158
|
-
<div class="global-color-group">
|
|
159
|
-
<div class="global-color-swatch" style="background: hsl({$editorState.overlays.globals.overlay.hue}, {$editorState.overlays.globals.overlay.saturation}%, {$editorState.overlays.globals.overlay.lightness}%);"></div>
|
|
160
|
-
<div class="global-color-sliders">
|
|
161
|
-
<div class="global-shadow-row">
|
|
162
|
-
<span class="shadow-slider-label">H</span>
|
|
163
|
-
<div class="slider-track" style="background: {overlayHueGrad($editorState.overlays.globals.overlay)}">
|
|
164
|
-
<input type="range" min="0" max="360" value={$editorState.overlays.globals.overlay.hue}
|
|
165
|
-
onpointerdown={() => beginSliderGesture('overlay hue')}
|
|
166
|
-
oninput={(e) => setOverlayColor('overlay', 'hue', +e.currentTarget.value)} />
|
|
167
|
-
</div>
|
|
168
|
-
<input class="shadow-slider-input" type="number" min="0" max="360"
|
|
169
|
-
value={$editorState.overlays.globals.overlay.hue}
|
|
170
|
-
onchange={(e) => setOverlayColor('overlay', 'hue', +e.currentTarget.value)} />
|
|
171
|
-
<span class="shadow-slider-unit">°</span>
|
|
172
|
-
</div>
|
|
173
|
-
<div class="global-shadow-row">
|
|
174
|
-
<span class="shadow-slider-label">S</span>
|
|
175
|
-
<div class="slider-track" style="background: {overlaySatGrad($editorState.overlays.globals.overlay)}">
|
|
176
|
-
<input type="range" min="0" max="100" value={$editorState.overlays.globals.overlay.saturation}
|
|
177
|
-
onpointerdown={() => beginSliderGesture('overlay saturation')}
|
|
178
|
-
oninput={(e) => setOverlayColor('overlay', 'saturation', +e.currentTarget.value)} />
|
|
179
|
-
</div>
|
|
180
|
-
<input class="shadow-slider-input" type="number" min="0" max="100"
|
|
181
|
-
value={$editorState.overlays.globals.overlay.saturation}
|
|
182
|
-
onchange={(e) => setOverlayColor('overlay', 'saturation', +e.currentTarget.value)} />
|
|
183
|
-
<span class="shadow-slider-unit">%</span>
|
|
184
|
-
</div>
|
|
185
|
-
<div class="global-shadow-row">
|
|
186
|
-
<span class="shadow-slider-label">L</span>
|
|
187
|
-
<div class="slider-track" style="background: {overlayLightGrad($editorState.overlays.globals.overlay)}">
|
|
188
|
-
<input type="range" min="0" max="100" value={$editorState.overlays.globals.overlay.lightness}
|
|
189
|
-
onpointerdown={() => beginSliderGesture('overlay lightness')}
|
|
190
|
-
oninput={(e) => setOverlayColor('overlay', 'lightness', +e.currentTarget.value)} />
|
|
191
|
-
</div>
|
|
192
|
-
<input class="shadow-slider-input" type="number" min="0" max="100"
|
|
193
|
-
value={$editorState.overlays.globals.overlay.lightness}
|
|
194
|
-
onchange={(e) => setOverlayColor('overlay', 'lightness', +e.currentTarget.value)} />
|
|
195
|
-
<span class="shadow-slider-unit">%</span>
|
|
196
|
-
</div>
|
|
197
|
-
</div>
|
|
198
|
-
</div>
|
|
199
|
-
</div>
|
|
200
|
-
<div class="overlay-global-col">
|
|
201
|
-
<div class="global-shadow-row">
|
|
202
|
-
<span class="shadow-slider-label">Op. Min</span>
|
|
203
|
-
<input type="range" min="0" max="100" value={Math.round($editorState.overlays.globals.overlay.opacityMin * 100)}
|
|
204
|
-
onpointerdown={() => beginSliderGesture('overlay opacity min')}
|
|
205
|
-
oninput={(e) => setOverlayOpacity('overlay', 'opacityMin', +e.currentTarget.value / 100)} />
|
|
206
|
-
<input class="shadow-slider-input" type="number" min="0" max="100"
|
|
207
|
-
value={Math.round($editorState.overlays.globals.overlay.opacityMin * 100)}
|
|
208
|
-
onchange={(e) => setOverlayOpacity('overlay', 'opacityMin', Math.min(100, Math.max(0, +e.currentTarget.value)) / 100)} />
|
|
209
|
-
<span class="shadow-slider-unit">%</span>
|
|
210
|
-
</div>
|
|
211
|
-
<div class="global-shadow-row">
|
|
212
|
-
<span class="shadow-slider-label">Op. Max</span>
|
|
213
|
-
<input type="range" min="0" max="100" value={Math.round($editorState.overlays.globals.overlay.opacityMax * 100)}
|
|
214
|
-
onpointerdown={() => beginSliderGesture('overlay opacity max')}
|
|
215
|
-
oninput={(e) => setOverlayOpacity('overlay', 'opacityMax', +e.currentTarget.value / 100)} />
|
|
216
|
-
<input class="shadow-slider-input" type="number" min="0" max="100"
|
|
217
|
-
value={Math.round($editorState.overlays.globals.overlay.opacityMax * 100)}
|
|
218
|
-
onchange={(e) => setOverlayOpacity('overlay', 'opacityMax', Math.min(100, Math.max(0, +e.currentTarget.value)) / 100)} />
|
|
219
|
-
<span class="shadow-slider-unit">%</span>
|
|
220
|
-
</div>
|
|
221
|
-
</div>
|
|
222
|
-
</div>
|
|
223
|
-
</div>
|
|
224
|
-
|
|
225
|
-
<h3 class="group-title" style="margin-top: var(--ui-space-16);">Hover Overlays</h3>
|
|
94
|
+
<h3 class="group-title">Hover Overlays</h3>
|
|
226
95
|
<div class="overlays-grid">
|
|
227
|
-
{#each $editorState.overlays.hoverTokens as token, i}
|
|
228
|
-
<div class="overlay-
|
|
96
|
+
{#each $editorState.overlays.hoverTokens as token, i (token.variable)}
|
|
97
|
+
<div class="overlay-row">
|
|
229
98
|
<div class="overlay-swatch-wrap overlay-swatch-wrap--dark">
|
|
230
|
-
<div class="overlay-swatch" style="background: {
|
|
99
|
+
<div class="overlay-swatch" style="background: var({token.variable});"></div>
|
|
231
100
|
</div>
|
|
232
|
-
<div class="
|
|
233
|
-
<button
|
|
101
|
+
<div class="overlay-meta">
|
|
102
|
+
<button
|
|
103
|
+
class="token-variable copyable"
|
|
104
|
+
class:copied={copiedVar === token.variable}
|
|
105
|
+
onclick={() => copy(token.variable)}
|
|
106
|
+
>{copiedVar === token.variable ? 'copied!' : token.variable}</button>
|
|
234
107
|
<span class="token-value">{token.label} — {Math.round(token.opacity * 100)}%</span>
|
|
235
108
|
</div>
|
|
236
|
-
<
|
|
237
|
-
|
|
109
|
+
<div class="overlay-picker">
|
|
110
|
+
<UIPaletteSelector
|
|
111
|
+
variable={token.variable}
|
|
112
|
+
showNone={false}
|
|
113
|
+
onwrite={(v) => handleWrite('hover', i, v)}
|
|
114
|
+
/>
|
|
115
|
+
</div>
|
|
116
|
+
<button
|
|
117
|
+
class="copy-css-btn"
|
|
118
|
+
title="Copy resolved CSS"
|
|
119
|
+
onclick={() => copy(copyCss(token))}
|
|
120
|
+
>
|
|
121
|
+
{copiedVar === copyCss(token) ? 'copied!' : 'copy css'}
|
|
238
122
|
</button>
|
|
239
|
-
{#if editingOverlay === token.variable}
|
|
240
|
-
<div class="shadow-editor">
|
|
241
|
-
<div class="shadow-slider-row">
|
|
242
|
-
<span class="shadow-slider-label">Opacity</span>
|
|
243
|
-
<input type="range" min="0" max="100" value={Math.round(token.opacity * 100)}
|
|
244
|
-
onpointerdown={() => beginSliderGesture(`edit ${token.variable} opacity`)}
|
|
245
|
-
oninput={(e) => setOverlayTokenOpacity('hover', i, +e.currentTarget.value / 100)} />
|
|
246
|
-
<input class="shadow-slider-input" type="number" min="0" max="100"
|
|
247
|
-
value={Math.round(token.opacity * 100)}
|
|
248
|
-
onchange={(e) => setOverlayTokenOpacity('hover', i, Math.min(100, Math.max(0, +e.currentTarget.value)) / 100)} />
|
|
249
|
-
<span class="shadow-slider-unit">%</span>
|
|
250
|
-
</div>
|
|
251
|
-
<div class="shadow-css-output">
|
|
252
|
-
<code>{getOverlayCss(token)}</code>
|
|
253
|
-
<button class="shadow-copy-btn" onclick={() => copy(getOverlayCss(token))}>
|
|
254
|
-
{copiedVar === getOverlayCss(token) ? 'Copied!' : 'Copy CSS'}
|
|
255
|
-
</button>
|
|
256
|
-
</div>
|
|
257
|
-
</div>
|
|
258
|
-
{/if}
|
|
259
123
|
</div>
|
|
260
124
|
{/each}
|
|
261
125
|
</div>
|
|
262
|
-
|
|
263
|
-
<!-- Global hover editor -->
|
|
264
|
-
<div class="overlay-global-editor">
|
|
265
|
-
<h4 class="global-shadow-title">Global Hover Controls</h4>
|
|
266
|
-
<div class="overlay-global-columns">
|
|
267
|
-
<div class="overlay-global-col">
|
|
268
|
-
<div class="global-color-group">
|
|
269
|
-
<div class="global-color-swatch" style="background: hsl({$editorState.overlays.globals.hover.hue}, {$editorState.overlays.globals.hover.saturation}%, {$editorState.overlays.globals.hover.lightness}%);"></div>
|
|
270
|
-
<div class="global-color-sliders">
|
|
271
|
-
<div class="global-shadow-row">
|
|
272
|
-
<span class="shadow-slider-label">H</span>
|
|
273
|
-
<div class="slider-track" style="background: {overlayHueGrad($editorState.overlays.globals.hover)}">
|
|
274
|
-
<input type="range" min="0" max="360" value={$editorState.overlays.globals.hover.hue}
|
|
275
|
-
onpointerdown={() => beginSliderGesture('hover hue')}
|
|
276
|
-
oninput={(e) => setOverlayColor('hover', 'hue', +e.currentTarget.value)} />
|
|
277
|
-
</div>
|
|
278
|
-
<input class="shadow-slider-input" type="number" min="0" max="360"
|
|
279
|
-
value={$editorState.overlays.globals.hover.hue}
|
|
280
|
-
onchange={(e) => setOverlayColor('hover', 'hue', +e.currentTarget.value)} />
|
|
281
|
-
<span class="shadow-slider-unit">°</span>
|
|
282
|
-
</div>
|
|
283
|
-
<div class="global-shadow-row">
|
|
284
|
-
<span class="shadow-slider-label">S</span>
|
|
285
|
-
<div class="slider-track" style="background: {overlaySatGrad($editorState.overlays.globals.hover)}">
|
|
286
|
-
<input type="range" min="0" max="100" value={$editorState.overlays.globals.hover.saturation}
|
|
287
|
-
onpointerdown={() => beginSliderGesture('hover saturation')}
|
|
288
|
-
oninput={(e) => setOverlayColor('hover', 'saturation', +e.currentTarget.value)} />
|
|
289
|
-
</div>
|
|
290
|
-
<input class="shadow-slider-input" type="number" min="0" max="100"
|
|
291
|
-
value={$editorState.overlays.globals.hover.saturation}
|
|
292
|
-
onchange={(e) => setOverlayColor('hover', 'saturation', +e.currentTarget.value)} />
|
|
293
|
-
<span class="shadow-slider-unit">%</span>
|
|
294
|
-
</div>
|
|
295
|
-
<div class="global-shadow-row">
|
|
296
|
-
<span class="shadow-slider-label">L</span>
|
|
297
|
-
<div class="slider-track" style="background: {overlayLightGrad($editorState.overlays.globals.hover)}">
|
|
298
|
-
<input type="range" min="0" max="100" value={$editorState.overlays.globals.hover.lightness}
|
|
299
|
-
onpointerdown={() => beginSliderGesture('hover lightness')}
|
|
300
|
-
oninput={(e) => setOverlayColor('hover', 'lightness', +e.currentTarget.value)} />
|
|
301
|
-
</div>
|
|
302
|
-
<input class="shadow-slider-input" type="number" min="0" max="100"
|
|
303
|
-
value={$editorState.overlays.globals.hover.lightness}
|
|
304
|
-
onchange={(e) => setOverlayColor('hover', 'lightness', +e.currentTarget.value)} />
|
|
305
|
-
<span class="shadow-slider-unit">%</span>
|
|
306
|
-
</div>
|
|
307
|
-
</div>
|
|
308
|
-
</div>
|
|
309
|
-
</div>
|
|
310
|
-
<div class="overlay-global-col">
|
|
311
|
-
<div class="global-shadow-row">
|
|
312
|
-
<span class="shadow-slider-label">Op. Min</span>
|
|
313
|
-
<input type="range" min="0" max="100" value={Math.round($editorState.overlays.globals.hover.opacityMin * 100)}
|
|
314
|
-
onpointerdown={() => beginSliderGesture('hover opacity min')}
|
|
315
|
-
oninput={(e) => setOverlayOpacity('hover', 'opacityMin', +e.currentTarget.value / 100)} />
|
|
316
|
-
<input class="shadow-slider-input" type="number" min="0" max="100"
|
|
317
|
-
value={Math.round($editorState.overlays.globals.hover.opacityMin * 100)}
|
|
318
|
-
onchange={(e) => setOverlayOpacity('hover', 'opacityMin', Math.min(100, Math.max(0, +e.currentTarget.value)) / 100)} />
|
|
319
|
-
<span class="shadow-slider-unit">%</span>
|
|
320
|
-
</div>
|
|
321
|
-
<div class="global-shadow-row">
|
|
322
|
-
<span class="shadow-slider-label">Op. Max</span>
|
|
323
|
-
<input type="range" min="0" max="100" value={Math.round($editorState.overlays.globals.hover.opacityMax * 100)}
|
|
324
|
-
onpointerdown={() => beginSliderGesture('hover opacity max')}
|
|
325
|
-
oninput={(e) => setOverlayOpacity('hover', 'opacityMax', +e.currentTarget.value / 100)} />
|
|
326
|
-
<input class="shadow-slider-input" type="number" min="0" max="100"
|
|
327
|
-
value={Math.round($editorState.overlays.globals.hover.opacityMax * 100)}
|
|
328
|
-
onchange={(e) => setOverlayOpacity('hover', 'opacityMax', Math.min(100, Math.max(0, +e.currentTarget.value)) / 100)} />
|
|
329
|
-
<span class="shadow-slider-unit">%</span>
|
|
330
|
-
</div>
|
|
331
|
-
</div>
|
|
332
|
-
</div>
|
|
333
|
-
</div>
|
|
334
126
|
</section>
|
|
335
127
|
|
|
336
128
|
<style>
|
|
@@ -356,52 +148,27 @@
|
|
|
356
148
|
margin: 0;
|
|
357
149
|
}
|
|
358
150
|
|
|
359
|
-
.
|
|
151
|
+
.overlays-grid {
|
|
360
152
|
display: flex;
|
|
361
153
|
flex-direction: column;
|
|
362
|
-
gap: var(--ui-space-
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
.token-variable.copyable {
|
|
366
|
-
all: unset;
|
|
367
|
-
font-size: var(--ui-font-size-md);
|
|
368
|
-
color: var(--ui-text-tertiary);
|
|
369
|
-
font-family: var(--ui-font-mono);
|
|
370
|
-
cursor: pointer;
|
|
371
|
-
transition: color var(--ui-transition-fast);
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
.token-variable.copyable:hover {
|
|
375
|
-
color: var(--ui-text-accent);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
.token-variable.copyable.copied {
|
|
379
|
-
color: var(--ui-text-success);
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
.token-value {
|
|
383
|
-
font-size: var(--ui-font-size-md);
|
|
384
|
-
color: var(--ui-text-muted);
|
|
154
|
+
gap: var(--ui-space-8);
|
|
385
155
|
}
|
|
386
156
|
|
|
387
|
-
|
|
388
|
-
.overlays-grid {
|
|
157
|
+
.overlay-row {
|
|
389
158
|
display: grid;
|
|
390
|
-
grid-template-columns:
|
|
391
|
-
gap: var(--ui-space-
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
.overlay-item {
|
|
395
|
-
display: flex;
|
|
396
|
-
flex-direction: column;
|
|
159
|
+
grid-template-columns: 3.5rem minmax(10rem, 1fr) minmax(14rem, 1.5fr) auto;
|
|
160
|
+
gap: var(--ui-space-12);
|
|
397
161
|
align-items: center;
|
|
398
|
-
|
|
162
|
+
padding: var(--ui-space-8) var(--ui-space-12);
|
|
163
|
+
background: var(--ui-surface-lowest);
|
|
164
|
+
border: 1px solid var(--ui-border-low);
|
|
165
|
+
border-radius: var(--ui-radius-md);
|
|
399
166
|
}
|
|
400
167
|
|
|
401
168
|
.overlay-swatch-wrap {
|
|
402
|
-
width:
|
|
403
|
-
height:
|
|
404
|
-
border-radius: var(--ui-radius-
|
|
169
|
+
width: 3.5rem;
|
|
170
|
+
height: 3.5rem;
|
|
171
|
+
border-radius: var(--ui-radius-sm);
|
|
405
172
|
position: relative;
|
|
406
173
|
overflow: hidden;
|
|
407
174
|
border: 1px solid var(--ui-border-low);
|
|
@@ -422,8 +189,6 @@
|
|
|
422
189
|
linear-gradient(-45deg, #333 25%, transparent 25%),
|
|
423
190
|
linear-gradient(45deg, transparent 75%, #333 75%),
|
|
424
191
|
linear-gradient(-45deg, transparent 75%, #333 75%);
|
|
425
|
-
background-size: 12px 12px;
|
|
426
|
-
background-position: 0 0, 0 6px, 6px -6px, -6px 0px;
|
|
427
192
|
}
|
|
428
193
|
|
|
429
194
|
.overlay-swatch {
|
|
@@ -431,162 +196,33 @@
|
|
|
431
196
|
inset: 0;
|
|
432
197
|
}
|
|
433
198
|
|
|
434
|
-
.overlay-
|
|
435
|
-
align-items: center;
|
|
436
|
-
text-align: center;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
.overlay-global-editor {
|
|
440
|
-
display: flex;
|
|
441
|
-
flex-direction: column;
|
|
442
|
-
gap: var(--ui-space-8);
|
|
443
|
-
padding: var(--ui-space-12);
|
|
444
|
-
background: var(--ui-surface-lowest);
|
|
445
|
-
border: 1px solid var(--ui-border-low);
|
|
446
|
-
border-radius: var(--ui-radius-md);
|
|
447
|
-
margin-top: var(--ui-space-8);
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
.overlay-global-columns {
|
|
451
|
-
display: grid;
|
|
452
|
-
grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr));
|
|
453
|
-
gap: var(--ui-space-16);
|
|
454
|
-
align-items: start;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
.overlay-global-col {
|
|
199
|
+
.overlay-meta {
|
|
458
200
|
display: flex;
|
|
459
201
|
flex-direction: column;
|
|
460
|
-
gap: var(--ui-space-
|
|
202
|
+
gap: var(--ui-space-2);
|
|
461
203
|
min-width: 0;
|
|
462
204
|
}
|
|
463
205
|
|
|
464
|
-
.
|
|
465
|
-
|
|
466
|
-
font-
|
|
467
|
-
color: var(--ui-text-secondary);
|
|
468
|
-
margin: 0;
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
.global-shadow-row {
|
|
472
|
-
display: flex;
|
|
473
|
-
align-items: center;
|
|
474
|
-
gap: var(--ui-space-8);
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
.global-shadow-row .shadow-slider-label {
|
|
478
|
-
width: 3rem;
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
.global-shadow-row input[type="range"] {
|
|
482
|
-
flex: 1;
|
|
483
|
-
min-width: 4rem;
|
|
484
|
-
accent-color: var(--ui-text-accent);
|
|
485
|
-
height: 4px;
|
|
486
|
-
cursor: pointer;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
.shadow-slider-row {
|
|
490
|
-
display: flex;
|
|
491
|
-
align-items: center;
|
|
492
|
-
gap: var(--ui-space-8);
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
.shadow-slider-row input[type="range"] {
|
|
496
|
-
flex: 1;
|
|
497
|
-
min-width: 5rem;
|
|
498
|
-
accent-color: var(--ui-text-accent);
|
|
499
|
-
height: 4px;
|
|
500
|
-
cursor: pointer;
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
.shadow-slider-label {
|
|
504
|
-
font-size: var(--ui-font-size-xs);
|
|
505
|
-
font-weight: var(--ui-font-weight-semibold);
|
|
206
|
+
.token-variable.copyable {
|
|
207
|
+
all: unset;
|
|
208
|
+
font-size: var(--ui-font-size-md);
|
|
506
209
|
color: var(--ui-text-tertiary);
|
|
507
|
-
width: 4rem;
|
|
508
|
-
text-align: right;
|
|
509
|
-
flex-shrink: 0;
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
.shadow-slider-input {
|
|
513
|
-
font-size: var(--ui-font-size-xs);
|
|
514
|
-
color: var(--ui-text-primary);
|
|
515
|
-
font-family: var(--ui-font-mono);
|
|
516
|
-
width: 2.5rem;
|
|
517
|
-
text-align: right;
|
|
518
|
-
flex-shrink: 0;
|
|
519
|
-
background: var(--ui-surface-lowest);
|
|
520
|
-
border: 1px solid var(--ui-border-low);
|
|
521
|
-
border-radius: var(--ui-radius-sm);
|
|
522
|
-
padding: var(--ui-space-2) var(--ui-space-4);
|
|
523
|
-
-moz-appearance: textfield;
|
|
524
|
-
appearance: textfield;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
.shadow-slider-input::-webkit-inner-spin-button,
|
|
528
|
-
.shadow-slider-input::-webkit-outer-spin-button {
|
|
529
|
-
-webkit-appearance: none;
|
|
530
|
-
margin: 0;
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
.shadow-slider-input:focus {
|
|
534
|
-
outline: none;
|
|
535
|
-
border-color: var(--ui-border-high);
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
.shadow-slider-unit {
|
|
539
|
-
font-size: var(--ui-font-size-xs);
|
|
540
|
-
color: var(--ui-text-muted);
|
|
541
210
|
font-family: var(--ui-font-mono);
|
|
542
|
-
width: 1rem;
|
|
543
|
-
flex-shrink: 0;
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
.shadow-edit-btn {
|
|
547
|
-
all: unset;
|
|
548
|
-
font-size: var(--ui-font-size-xs);
|
|
549
|
-
color: var(--ui-text-muted);
|
|
550
211
|
cursor: pointer;
|
|
551
|
-
|
|
552
|
-
border-radius: var(--ui-radius-sm);
|
|
553
|
-
transition: color var(--ui-transition-fast), background var(--ui-transition-fast);
|
|
212
|
+
transition: color var(--ui-transition-fast);
|
|
554
213
|
}
|
|
555
214
|
|
|
556
|
-
.
|
|
557
|
-
|
|
558
|
-
background: var(--ui-surface-low);
|
|
559
|
-
}
|
|
215
|
+
.token-variable.copyable:hover { color: var(--ui-text-accent); }
|
|
216
|
+
.token-variable.copyable.copied { color: var(--ui-text-success); }
|
|
560
217
|
|
|
561
|
-
.
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
gap: var(--ui-space-8);
|
|
565
|
-
padding: var(--ui-space-12);
|
|
566
|
-
background: var(--ui-surface-lowest);
|
|
567
|
-
border: 1px solid var(--ui-border-low);
|
|
568
|
-
border-radius: var(--ui-radius-md);
|
|
569
|
-
width: 100%;
|
|
570
|
-
min-width: 14rem;
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
.shadow-css-output {
|
|
574
|
-
display: flex;
|
|
575
|
-
flex-direction: column;
|
|
576
|
-
gap: var(--ui-space-4);
|
|
577
|
-
margin-top: var(--ui-space-4);
|
|
578
|
-
padding-top: var(--ui-space-8);
|
|
579
|
-
border-top: 1px solid var(--ui-border-low);
|
|
218
|
+
.token-value {
|
|
219
|
+
font-size: var(--ui-font-size-md);
|
|
220
|
+
color: var(--ui-text-muted);
|
|
580
221
|
}
|
|
581
222
|
|
|
582
|
-
.
|
|
583
|
-
font-size: var(--ui-font-size-xs);
|
|
584
|
-
color: var(--ui-text-accent);
|
|
585
|
-
font-family: var(--ui-font-mono);
|
|
586
|
-
word-break: break-all;
|
|
587
|
-
}
|
|
223
|
+
.overlay-picker { min-width: 0; }
|
|
588
224
|
|
|
589
|
-
.
|
|
225
|
+
.copy-css-btn {
|
|
590
226
|
all: unset;
|
|
591
227
|
font-size: var(--ui-font-size-xs);
|
|
592
228
|
color: var(--ui-text-muted);
|
|
@@ -594,80 +230,11 @@
|
|
|
594
230
|
padding: var(--ui-space-2) var(--ui-space-6);
|
|
595
231
|
border: 1px solid var(--ui-border-low);
|
|
596
232
|
border-radius: var(--ui-radius-sm);
|
|
597
|
-
text-align: center;
|
|
598
233
|
transition: color var(--ui-transition-fast), border-color var(--ui-transition-fast);
|
|
599
234
|
}
|
|
600
235
|
|
|
601
|
-
.
|
|
236
|
+
.copy-css-btn:hover {
|
|
602
237
|
color: var(--ui-text-primary);
|
|
603
238
|
border-color: var(--ui-border-high);
|
|
604
239
|
}
|
|
605
|
-
|
|
606
|
-
.slider-track {
|
|
607
|
-
flex: 1;
|
|
608
|
-
min-width: 4rem;
|
|
609
|
-
position: relative;
|
|
610
|
-
height: 12px;
|
|
611
|
-
border-radius: var(--ui-radius-sm);
|
|
612
|
-
border: 1px solid var(--ui-border-low);
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
.slider-track input[type="range"] {
|
|
616
|
-
position: absolute;
|
|
617
|
-
top: 0;
|
|
618
|
-
left: 0;
|
|
619
|
-
width: 100%;
|
|
620
|
-
height: 100%;
|
|
621
|
-
cursor: pointer;
|
|
622
|
-
margin: 0;
|
|
623
|
-
-webkit-appearance: none;
|
|
624
|
-
appearance: none;
|
|
625
|
-
background: transparent;
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
.slider-track input[type="range"]::-webkit-slider-thumb {
|
|
629
|
-
-webkit-appearance: none;
|
|
630
|
-
width: 10px;
|
|
631
|
-
height: 14px;
|
|
632
|
-
border-radius: 2px;
|
|
633
|
-
background: white;
|
|
634
|
-
border: 1px solid var(--ui-border-low);
|
|
635
|
-
box-shadow: 0 1px 3px rgba(0,0,0,0.4);
|
|
636
|
-
cursor: pointer;
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
.slider-track input[type="range"]::-moz-range-thumb {
|
|
640
|
-
width: 10px;
|
|
641
|
-
height: 14px;
|
|
642
|
-
border-radius: 2px;
|
|
643
|
-
background: white;
|
|
644
|
-
border: 1px solid var(--ui-border-low);
|
|
645
|
-
box-shadow: 0 1px 3px rgba(0,0,0,0.4);
|
|
646
|
-
cursor: pointer;
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
.slider-track input[type="range"]::-moz-range-track {
|
|
650
|
-
background: transparent;
|
|
651
|
-
border: none;
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
.global-color-group {
|
|
655
|
-
display: flex;
|
|
656
|
-
gap: var(--ui-space-8);
|
|
657
|
-
align-items: stretch;
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
.global-color-swatch {
|
|
661
|
-
width: 2rem;
|
|
662
|
-
flex-shrink: 0;
|
|
663
|
-
border-radius: var(--ui-radius-sm);
|
|
664
|
-
border: 1px solid var(--ui-border-low);
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
.global-color-sliders {
|
|
668
|
-
flex: 1;
|
|
669
|
-
display: flex;
|
|
670
|
-
flex-direction: column;
|
|
671
|
-
gap: var(--ui-space-6);
|
|
672
|
-
}
|
|
673
240
|
</style>
|