@motion-proto/live-tokens 0.58.0 → 0.59.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 (30) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/dist-plugin/generateColorsAndType/index.cjs +4 -3
  3. package/dist-plugin/generateColorsAndType/index.js +4 -3
  4. package/package.json +1 -1
  5. package/src/editor/component-editor/ImageLightboxEditor.svelte +11 -2
  6. package/src/editor/core/sketch/sketchPresets.ts +8 -8
  7. package/src/editor/core/themes/parsers/shadow.ts +12 -4
  8. package/src/editor/ui/ColorEditPanel.svelte +13 -9
  9. package/src/editor/ui/PaletteEditor.svelte +11 -3
  10. package/src/editor/ui/UIInlineEditActions.svelte +97 -0
  11. package/src/live-tokens/data/colors-and-type/autumn.json +5 -5
  12. package/src/live-tokens/data/colors-and-type/default.json +5 -5
  13. package/src/live-tokens/data/colors-and-type/halloween.json +5 -5
  14. package/src/live-tokens/data/colors-and-type/midnight-study.json +5 -5
  15. package/src/live-tokens/data/colors-and-type/ocean.json +5 -5
  16. package/src/live-tokens/data/colors-and-type/royal-velvet.json +5 -5
  17. package/src/live-tokens/data/colors-and-type/sketchy.json +5 -5
  18. package/src/live-tokens/data/colors-and-type/spring-meadow.json +5 -5
  19. package/src/live-tokens/data/colors-and-type/sunset.json +5 -5
  20. package/src/live-tokens/data/themes/autumn.json +5 -5
  21. package/src/live-tokens/data/themes/halloween.json +5 -5
  22. package/src/live-tokens/data/themes/midnight-study.json +5 -5
  23. package/src/live-tokens/data/themes/ocean.json +5 -5
  24. package/src/live-tokens/data/themes/royal-velvet.json +5 -5
  25. package/src/live-tokens/data/themes/sketchy.json +5 -5
  26. package/src/live-tokens/data/themes/spring-meadow.json +5 -5
  27. package/src/live-tokens/data/themes/sunset.json +5 -5
  28. package/src/live-tokens/data/tokens.generated.css +5 -5
  29. package/src/system/components/ImageLightbox.svelte +26 -2
  30. package/src/system/styles/tokens.css +5 -5
package/CHANGELOG.md CHANGED
@@ -1,5 +1,54 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.59.0 — The shadow traces the art
4
+
5
+ ### Added
6
+
7
+ - **`shadow` on `ImageLightbox`.** The tile cast its shadow from its own
8
+ rectangle and offered no way out, so a PNG or WebP with transparency sat on a
9
+ phantom slab: the shadow drew the box, and the box was not the picture. Art
10
+ cut out against the page — a device mockup, a logo, a chart on no background —
11
+ showed a hard rounded rectangle behind it. `shadow` now picks where that
12
+ shadow falls. `box` is the old behaviour and stays the default. `content`
13
+ casts from the image's own alpha, so the shadow follows the silhouette.
14
+ `none` drops it. All three read the one `--imagelightbox-tile-shadow` the
15
+ theme already sets — the mode moves the shadow, it does not restyle it. The
16
+ open modal keeps its shadow only under `box`: it casts against a near-opaque
17
+ scrim where nothing reads, and filtering the pan-and-zoom surface would
18
+ repaint it every frame.
19
+
20
+ ### Changed
21
+
22
+ - **A shadow token writes its spread slot only when the spread is set.**
23
+ `--shadow-md` read `3px 3px 6px 0px hsla(…)`, and that fourth length was the
24
+ reason a shadow token could not also be a filter: `drop-shadow()` has no
25
+ spread slot, so the declaration was invalid and dropped. Every shipped theme
26
+ carries a zero spread, so the slot said nothing and cost the scale half its
27
+ reach. The zero-spread form is now three lengths, which `box-shadow` reads
28
+ identically and `drop-shadow()` accepts — one token, both properties, no
29
+ second scale to keep in step. The spread control is untouched: dial one and
30
+ the token grows its fourth length back, still a shadow but no longer a
31
+ filter, so a theme with a real spread has no shadow under `content`.
32
+ Four-length values still parse, and normalise on the next save.
33
+
34
+ ### Fixed
35
+
36
+ - **The colour edit panel wears editor chrome again.** Its confirm, cancel and
37
+ "Remove override" buttons were the shipped `Button` and `InlineEditActions`
38
+ components, which read the theme's own tokens — so the controls you edit a
39
+ theme *with* restyled themselves as you edited, and a saturated success or
40
+ danger palette turned three small chrome buttons into slabs. They are now
41
+ `UIInlineEditActions` and `UIPillButton`, drawn from the theme-immune `--ui-*`
42
+ scale like the rest of the editor. `UIInlineEditActions` is the editor's
43
+ check/cross pair, sized to the 1.5rem chrome buttons it sits beside.
44
+ - **The base colour panel offers a confirm and a cancel.** Opening a palette's
45
+ editor showed the base panel with no way to accept or discard — every slider
46
+ wrote straight through, and only the docked step panel carried the pair. The
47
+ panel now shows both, greyed until an edit is pending; the first touch of a
48
+ slider, a hex field or the eyedropper opens the same session the step panel
49
+ uses, so cancel restores the colour the panel opened with and confirm keeps
50
+ the drag as one undo step.
51
+
3
52
  ## 0.58.0 — The sketch layer states its contract
4
53
 
5
54
  ### Added
@@ -719,15 +719,16 @@ function computeAngleDistance(x, y) {
719
719
  return { angle: Math.round(angle), distance };
720
720
  }
721
721
  function shadowTokenCss(t) {
722
- return `${t.x}px ${t.y}px ${t.blur}px ${t.spread}px hsla(${t.hue}, ${t.saturation}%, ${t.lightness}%, ${t.opacity})`;
722
+ const spread = t.spread ? `${t.spread}px ` : "";
723
+ return `${t.x}px ${t.y}px ${t.blur}px ${spread}hsla(${t.hue}, ${t.saturation}%, ${t.lightness}%, ${t.opacity})`;
723
724
  }
724
725
  function parseShadowCss(variable, raw) {
725
- const m = raw.trim().match(/^(-?\d+)px\s+(-?\d+)px\s+(\d+)px\s+(-?\d+)px\s+hsla\(([\d.]+),\s*([\d.]+)%,\s*([\d.]+)%,\s*([\d.]+)\)$/);
726
+ const m = raw.trim().match(/^(-?\d+)px\s+(-?\d+)px\s+(\d+)px\s+(?:(-?\d+)px\s+)?hsla\(([\d.]+),\s*([\d.]+)%,\s*([\d.]+)%,\s*([\d.]+)\)$/);
726
727
  if (!m) return null;
727
728
  const x = parseInt(m[1], 10);
728
729
  const y = parseInt(m[2], 10);
729
730
  const blur = parseInt(m[3], 10);
730
- const spread = parseInt(m[4], 10);
731
+ const spread = m[4] === void 0 ? 0 : parseInt(m[4], 10);
731
732
  const hue = Math.round(parseFloat(m[5]));
732
733
  const saturation = Math.round(parseFloat(m[6]));
733
734
  const lightness = Math.round(parseFloat(m[7]));
@@ -105,15 +105,16 @@ function computeAngleDistance(x, y) {
105
105
  return { angle: Math.round(angle), distance };
106
106
  }
107
107
  function shadowTokenCss(t) {
108
- return `${t.x}px ${t.y}px ${t.blur}px ${t.spread}px hsla(${t.hue}, ${t.saturation}%, ${t.lightness}%, ${t.opacity})`;
108
+ const spread = t.spread ? `${t.spread}px ` : "";
109
+ return `${t.x}px ${t.y}px ${t.blur}px ${spread}hsla(${t.hue}, ${t.saturation}%, ${t.lightness}%, ${t.opacity})`;
109
110
  }
110
111
  function parseShadowCss(variable, raw) {
111
- const m = raw.trim().match(/^(-?\d+)px\s+(-?\d+)px\s+(\d+)px\s+(-?\d+)px\s+hsla\(([\d.]+),\s*([\d.]+)%,\s*([\d.]+)%,\s*([\d.]+)\)$/);
112
+ const m = raw.trim().match(/^(-?\d+)px\s+(-?\d+)px\s+(\d+)px\s+(?:(-?\d+)px\s+)?hsla\(([\d.]+),\s*([\d.]+)%,\s*([\d.]+)%,\s*([\d.]+)\)$/);
112
113
  if (!m) return null;
113
114
  const x = parseInt(m[1], 10);
114
115
  const y = parseInt(m[2], 10);
115
116
  const blur = parseInt(m[3], 10);
116
- const spread = parseInt(m[4], 10);
117
+ const spread = m[4] === void 0 ? 0 : parseInt(m[4], 10);
117
118
  const hue = Math.round(parseFloat(m[5]));
118
119
  const saturation = Math.round(parseFloat(m[6]));
119
120
  const lightness = Math.round(parseFloat(m[7]));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@motion-proto/live-tokens",
3
- "version": "0.58.0",
3
+ "version": "0.59.0",
4
4
  "type": "module",
5
5
  "description": "Design token editor with live CSS variable editing. Svelte 5 + Vite 8.",
6
6
  "keywords": [
@@ -44,12 +44,13 @@
44
44
  ];
45
45
 
46
46
  let multiple = $state(true);
47
+ let shadow = $state<'box' | 'content' | 'none'>('box');
47
48
  </script>
48
49
 
49
50
  <ComponentEditorBase
50
51
  {component}
51
52
  title="Image Lightbox"
52
- description="Click an inline image to expand it into a centered modal with a backdrop. Pass multiple images for a gallery (chevrons + counter). Extended mode adds zoom controls and drag panning."
53
+ description="Click an inline image to expand it into a centered modal with a backdrop. Pass multiple images for a gallery (chevrons + counter). Extended mode adds zoom controls and drag panning. `shadow` picks where the one tile-shadow token falls: `box` casts it from the tile rectangle, `content` from the image's own alpha, or `none`."
53
54
  tokens={allTokens}
54
55
  >
55
56
  <VariantGroup name="imagelightbox" title="Image Lightbox" {states} {component}>
@@ -58,9 +59,17 @@
58
59
  <input type="checkbox" checked={multiple} onchange={(e) => (multiple = e.currentTarget.checked)} />
59
60
  <span>Multiple images</span>
60
61
  </label>
62
+ <label class="preview-toggle">
63
+ <span>Shadow</span>
64
+ <select value={shadow} onchange={(e) => (shadow = e.currentTarget.value as typeof shadow)}>
65
+ <option value="box">Box</option>
66
+ <option value="content">Content</option>
67
+ <option value="none">None</option>
68
+ </select>
69
+ </label>
61
70
  {/snippet}
62
71
  <div class="preview-frame">
63
- <ImageLightbox images={multiple ? demoImages : [demoImages[0]]} extended />
72
+ <ImageLightbox images={multiple ? demoImages : [demoImages[0]]} {shadow} extended />
64
73
  </div>
65
74
  </VariantGroup>
66
75
  </ComponentEditorBase>
@@ -151,10 +151,10 @@ export const SKETCH_PRESETS: Record<string, SketchSettings> = {
151
151
  blurb: 'Broad translucent nib gone round twice on the same line, so the overlap darkens and the ink pools where it slows.',
152
152
  fillTravel: 2, strokeTravel: 1.5, wobble: 56, waveform: 1.4, borderWavelength: 1.3,
153
153
  strokeWidth: 4, doubleStroke: true, retracePass: 'copy', strokeInk: 0.52, retraceOffset: 2.2,
154
- maskBlob: 95, maskOutputMin: 0.5, maskOutputMax: 0.92, maskOctaves: 2, maskPosterize: 4, maskSoftness: 2.8,
154
+ maskBlob: 115, maskOutputMin: 0.21, maskOutputMax: 1, maskOctaves: 3, maskPosterize: 4, maskSoftness: 2.5,
155
155
  jitterX: 3, jitterY: 3, jitterRot: 0.8, jitterScale: 0.045,
156
156
  cornerSpread: 10, cornerTravel: 8,
157
- pressure: 0.2, pressureMod: 0.25, pooling: 2, iconTravel: 1.25,
157
+ pressure: 0.2, pressureMod: 0.25, pooling: 2, iconTravel: 1.25, iconMaskScale: 4,
158
158
  },
159
159
 
160
160
  whiteboard: {
@@ -196,22 +196,22 @@ export const SKETCH_PRESETS: Record<string, SketchSettings> = {
196
196
  blurb: 'Ballpoint in a hurry. Everything loose at once: a square wave sends every edge to full travel, and the second pass lands wherever it lands.',
197
197
  fillTravel: 3, strokeTravel: 2.25, wobble: 50, roughness: 3, waveform: 2.5,
198
198
  strokeWidth: 2.25, doubleStroke: true, retracePass: 'reseeded', retraceOffset: 4, strokeInk: 1,
199
- maskBlob: 150, maskOutputMin: 0.45, maskOutputMax: 1, maskOctaves: 2, maskPosterize: 2, maskSoftness: 6.7,
199
+ maskBlob: 150, maskOutputMin: 0.43, maskOutputMax: 0.95, maskOctaves: 2, maskPosterize: 2, maskSoftness: 6.7,
200
200
  jitterX: 6, jitterY: 6, jitterRot: 1.8, jitterScale: 0.1,
201
201
  cornerSpread: 20, cornerTravel: 17,
202
- pressure: 0.45, pressureMod: 0.6, pooling: 2.5, iconTravel: 2.25,
202
+ pressure: 0.45, pressureMod: 0.6, pooling: 2.5, iconTravel: 2.25, iconMaskScale: 3.6,
203
203
  },
204
204
 
205
205
  dry: {
206
206
  ...base, label: 'Dry marker',
207
- blurb: 'Ink that ran out. One scratchy pass that breaks up along its length, over a fill the mask has torn holes clean through.',
207
+ blurb: 'Ink that ran out. One scratchy pass that breaks up along its length, over a fill the mask has worn nearly through in patches.',
208
208
  fillTravel: 2.25, strokeTravel: 1.75, wobble: 50, waveform: 2, borderWavelength: 0.5,
209
209
  strokeWidth: 3.5, doubleStroke: false, strokeInk: 0.4,
210
- maskBlob: 150, maskOutputMin: 0, maskOutputMax: 0.85,
211
- maskOctaves: 2, maskPosterize: 4, maskSoftness: 2,
210
+ maskBlob: 150, maskOutputMin: 0.15, maskOutputMax: 0.91,
211
+ maskOctaves: 2, maskPosterize: 4, maskSoftness: 1.75,
212
212
  jitterX: 5, jitterY: 5, jitterRot: 1.4, jitterScale: 0.08,
213
213
  cornerSpread: 16, cornerTravel: 13,
214
- pressure: 0.4, pressureMod: 0.8, pooling: 1.5, iconTravel: 1.75, iconMaskScale: 0.65,
214
+ pressure: 0.4, pressureMod: 0.8, pooling: 1.5, iconTravel: 1.75, iconMaskScale: 3.8,
215
215
  },
216
216
  };
217
217
 
@@ -1,7 +1,14 @@
1
1
  /**
2
2
  * The single source of truth for the CSS form of a shadow scale token:
3
3
  *
4
- * <x>px <y>px <blur>px <spread>px hsla(<h>, <s>%, <l>%, <a>)
4
+ * <x>px <y>px <blur>px [<spread>px] hsla(<h>, <s>%, <l>%, <a>)
5
+ *
6
+ * The spread slot is written only when it is non-zero. A three-length shadow is
7
+ * legal in `box-shadow` (spread defaults to 0) and in `filter: drop-shadow()`,
8
+ * which has no spread slot at all — so one token dresses both, and a component
9
+ * casting from an image's alpha reads the same variable as one casting from a
10
+ * box. Dial a spread and the token grows its fourth length: still a shadow,
11
+ * no longer a filter.
5
12
  *
6
13
  * Everything that reads or writes that form goes through `parseShadowCss` /
7
14
  * `shadowTokenCss`, so the theme generator (Node, no store) and the editor
@@ -33,16 +40,17 @@ export function computeAngleDistance(x: number, y: number): { angle: number; dis
33
40
  }
34
41
 
35
42
  export function shadowTokenCss(t: ShadowValue): string {
36
- return `${t.x}px ${t.y}px ${t.blur}px ${t.spread}px hsla(${t.hue}, ${t.saturation}%, ${t.lightness}%, ${t.opacity})`;
43
+ const spread = t.spread ? `${t.spread}px ` : '';
44
+ return `${t.x}px ${t.y}px ${t.blur}px ${spread}hsla(${t.hue}, ${t.saturation}%, ${t.lightness}%, ${t.opacity})`;
37
45
  }
38
46
 
39
47
  export function parseShadowCss(variable: string, raw: string): ShadowToken | null {
40
- const m = raw.trim().match(/^(-?\d+)px\s+(-?\d+)px\s+(\d+)px\s+(-?\d+)px\s+hsla\(([\d.]+),\s*([\d.]+)%,\s*([\d.]+)%,\s*([\d.]+)\)$/);
48
+ const m = raw.trim().match(/^(-?\d+)px\s+(-?\d+)px\s+(\d+)px\s+(?:(-?\d+)px\s+)?hsla\(([\d.]+),\s*([\d.]+)%,\s*([\d.]+)%,\s*([\d.]+)\)$/);
41
49
  if (!m) return null;
42
50
  const x = parseInt(m[1], 10);
43
51
  const y = parseInt(m[2], 10);
44
52
  const blur = parseInt(m[3], 10);
45
- const spread = parseInt(m[4], 10);
53
+ const spread = m[4] === undefined ? 0 : parseInt(m[4], 10);
46
54
  const hue = Math.round(parseFloat(m[5]));
47
55
  const saturation = Math.round(parseFloat(m[6]));
48
56
  const lightness = Math.round(parseFloat(m[7]));
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { hexToOklch, oklchToHex, gamutClamp } from '../core/palettes/oklch';
3
- import InlineEditActions from '../../system/components/InlineEditActions.svelte';
4
- import Button from '../../system/components/Button.svelte';
3
+ import UIInlineEditActions from './UIInlineEditActions.svelte';
4
+ import UIPillButton from './UIPillButton.svelte';
5
5
 
6
6
 
7
7
 
@@ -15,6 +15,8 @@
15
15
  /** Hide the confirm/cancel actions for live-apply consumers (Colors view
16
16
  * readout) where edits commit through the store as they happen. */
17
17
  hideActions?: boolean;
18
+ /** Greys the confirm/cancel pair out while no edit is pending. */
19
+ actionsDisabled?: boolean;
18
20
  /** Hide the preview square where the surrounding view already shows the
19
21
  * color (Colors view: wheel, swatches and derived scale all do). */
20
22
  hidePreview?: boolean;
@@ -43,6 +45,7 @@
43
45
  highlighted = false,
44
46
  showRemoveOverride = false,
45
47
  hideActions = false,
48
+ actionsDisabled = false,
46
49
  hidePreview = false,
47
50
  onConfirm = () => {},
48
51
  onCancel = () => {},
@@ -171,16 +174,17 @@
171
174
  {#if !hideActions}
172
175
  <div class="hsl-panel-actions">
173
176
  {#if showRemoveOverride}
174
- <Button
175
- variant="danger"
176
- size="small"
177
- icon="fas fa-trash"
178
- on:click={onRemoveOverride}
179
- >Remove override</Button>
177
+ <UIPillButton
178
+ size="compact"
179
+ variant="outline"
180
+ icon="fa-trash"
181
+ onclick={onRemoveOverride}
182
+ >Remove override</UIPillButton>
180
183
  {/if}
181
- <InlineEditActions
184
+ <UIInlineEditActions
182
185
  onSave={onConfirm}
183
186
  onCancel={onCancel}
187
+ disabled={actionsDisabled}
184
188
  saveTitle="Apply changes"
185
189
  cancelTitle="Discard changes"
186
190
  />
@@ -203,6 +203,14 @@
203
203
 
204
204
  function startBaseEdit() {
205
205
  if (editing.kind === 'editingBase') { confirmEdit(); return; }
206
+ ensureBaseSession();
207
+ }
208
+
209
+ // The base panel applies every change straight to the store, so its
210
+ // confirm/cancel pair needs a session to act on. Opening one lazily on the
211
+ // first touch keeps the panel's mere presence from clipping the undo floor.
212
+ function ensureBaseSession() {
213
+ if (editing.kind === 'editingBase') return;
206
214
  editing = { kind: 'editingBase' };
207
215
  openSession();
208
216
  }
@@ -626,18 +634,18 @@
626
634
  <ColorEditPanel
627
635
  title={basePanelTitle}
628
636
  showRemoveOverride={false}
629
- hideActions={!isEditingBase}
637
+ actionsDisabled={!isEditingBase}
630
638
  hidePreview
631
639
  hue={baseColor.h}
632
640
  chroma={baseColor.c}
633
641
  lightness={baseColor.l * 100}
634
642
  chromaMax={BASE_CHROMA_MAX}
635
643
  chromaHint={neutral ? NEUTRAL_CALM_CHROMA : undefined}
636
- onHueChromaChange={(h, c, l) => setBaseColor(label, { l: l / 100, c, h })}
644
+ onHueChromaChange={(h, c, l) => { ensureBaseSession(); setBaseColor(label, { l: l / 100, c, h }); }}
637
645
  onConfirm={confirmEdit}
638
646
  onCancel={cancelEdit}
639
647
  onRemoveOverride={() => {}}
640
- onSliderStart={() => beginSliderGesture(`edit ${label} base`)}
648
+ onSliderStart={() => { ensureBaseSession(); beginSliderGesture(`edit ${label} base`); }}
641
649
  />
642
650
  </div>
643
651
  </div>
@@ -0,0 +1,97 @@
1
+ <script lang="ts">
2
+ interface Props {
3
+ onSave: () => void | Promise<void>;
4
+ onCancel: () => void;
5
+ disabled?: boolean;
6
+ saveTitle?: string;
7
+ cancelTitle?: string;
8
+ }
9
+
10
+ let {
11
+ onSave,
12
+ onCancel,
13
+ disabled = false,
14
+ saveTitle = 'Save',
15
+ cancelTitle = 'Cancel'
16
+ }: Props = $props();
17
+ </script>
18
+
19
+ <div class="ui-inline-actions">
20
+ <button
21
+ class="ui-ia ui-ia-save"
22
+ type="button"
23
+ {disabled}
24
+ title={saveTitle}
25
+ aria-label={saveTitle}
26
+ onclick={onSave}
27
+ ><i class="fas fa-check" aria-hidden="true"></i></button>
28
+ <button
29
+ class="ui-ia ui-ia-cancel"
30
+ type="button"
31
+ {disabled}
32
+ title={cancelTitle}
33
+ aria-label={cancelTitle}
34
+ onclick={onCancel}
35
+ ><i class="fas fa-xmark" aria-hidden="true"></i></button>
36
+ </div>
37
+
38
+ <style>
39
+ .ui-inline-actions {
40
+ display: inline-flex;
41
+ align-items: center;
42
+ gap: var(--ui-space-4);
43
+ }
44
+
45
+ /* Sized to the 1.5rem chrome buttons it sits beside (eyedropper, swatch). */
46
+ .ui-ia {
47
+ display: inline-flex;
48
+ align-items: center;
49
+ justify-content: center;
50
+ width: 1.5rem;
51
+ height: 1.5rem;
52
+ padding: 0;
53
+ border-radius: var(--ui-radius-sm);
54
+ font-size: var(--ui-font-size-xs);
55
+ line-height: 1;
56
+ cursor: pointer;
57
+ transition:
58
+ background var(--ui-transition-fast),
59
+ border-color var(--ui-transition-fast),
60
+ color var(--ui-transition-fast);
61
+ }
62
+
63
+ .ui-ia-save {
64
+ background: linear-gradient(180deg, rgba(90, 168, 94, 0.35) 0%, rgba(90, 168, 94, 0.18) 100%);
65
+ border: 1px solid rgba(90, 168, 94, 0.6);
66
+ color: var(--ui-text-primary);
67
+ }
68
+
69
+ .ui-ia-save:hover:not(:disabled) {
70
+ background: linear-gradient(180deg, rgba(90, 168, 94, 0.48) 0%, rgba(90, 168, 94, 0.26) 100%);
71
+ border-color: rgba(90, 168, 94, 0.78);
72
+ }
73
+
74
+ .ui-ia-cancel {
75
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.06) 0%, rgba(255, 255, 255, 0.02) 100%);
76
+ border: 1px solid rgba(255, 255, 255, 0.3);
77
+ color: var(--ui-text-secondary);
78
+ }
79
+
80
+ .ui-ia-cancel:hover:not(:disabled) {
81
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.12) 0%, rgba(255, 255, 255, 0.05) 100%);
82
+ border-color: rgba(255, 255, 255, 0.5);
83
+ color: var(--ui-text-primary);
84
+ }
85
+
86
+ .ui-ia:focus-visible {
87
+ outline: none;
88
+ box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.18);
89
+ }
90
+
91
+ .ui-ia:disabled {
92
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.04) 0%, rgba(255, 255, 255, 0.02) 100%);
93
+ border-color: rgba(255, 255, 255, 0.14);
94
+ color: var(--ui-text-disabled);
95
+ cursor: not-allowed;
96
+ }
97
+ </style>
@@ -2265,11 +2265,11 @@
2265
2265
  "--hover-low": "color-mix(in srgb, var(--text-primary) 5%, transparent)",
2266
2266
  "--hover": "color-mix(in srgb, var(--text-primary) 10%, transparent)",
2267
2267
  "--hover-high": "color-mix(in srgb, var(--text-primary) 15%, transparent)",
2268
- "--shadow-sm": "1px 1px 2px 0px hsla(237, 18%, 3%, 0.25)",
2269
- "--shadow-md": "3px 3px 6px 0px hsla(237, 18%, 3%, 0.25)",
2270
- "--shadow-lg": "5px 5px 9px 0px hsla(237, 18%, 3%, 0.25)",
2271
- "--shadow-xl": "6px 7px 13px 0px hsla(237, 18%, 3%, 0.25)",
2272
- "--shadow-2xl": "8px 9px 16px 0px hsla(237, 18%, 3%, 0.25)"
2268
+ "--shadow-sm": "1px 1px 2px hsla(237, 18%, 3%, 0.25)",
2269
+ "--shadow-md": "3px 3px 6px hsla(237, 18%, 3%, 0.25)",
2270
+ "--shadow-lg": "5px 5px 9px hsla(237, 18%, 3%, 0.25)",
2271
+ "--shadow-xl": "6px 7px 13px hsla(237, 18%, 3%, 0.25)",
2272
+ "--shadow-2xl": "8px 9px 16px hsla(237, 18%, 3%, 0.25)"
2273
2273
  },
2274
2274
  "fontSources": [
2275
2275
  {
@@ -2296,11 +2296,11 @@
2296
2296
  "--hover-low": "color-mix(in srgb, var(--text-primary) 5%, transparent)",
2297
2297
  "--hover": "color-mix(in srgb, var(--text-primary) 10%, transparent)",
2298
2298
  "--hover-high": "color-mix(in srgb, var(--text-primary) 15%, transparent)",
2299
- "--shadow-sm": "1px 1px 2px 0px hsla(237, 18%, 3%, 0.9)",
2300
- "--shadow-md": "3px 3px 6px 0px hsla(237, 18%, 3%, 0.9)",
2301
- "--shadow-lg": "5px 5px 9px 0px hsla(237, 18%, 3%, 0.9)",
2302
- "--shadow-xl": "6px 7px 13px 0px hsla(237, 18%, 3%, 0.9)",
2303
- "--shadow-2xl": "8px 9px 16px 0px hsla(237, 18%, 3%, 0.9)"
2299
+ "--shadow-sm": "1px 1px 2px hsla(237, 18%, 3%, 0.9)",
2300
+ "--shadow-md": "3px 3px 6px hsla(237, 18%, 3%, 0.9)",
2301
+ "--shadow-lg": "5px 5px 9px hsla(237, 18%, 3%, 0.9)",
2302
+ "--shadow-xl": "6px 7px 13px hsla(237, 18%, 3%, 0.9)",
2303
+ "--shadow-2xl": "8px 9px 16px hsla(237, 18%, 3%, 0.9)"
2304
2304
  },
2305
2305
  "fontSources": [
2306
2306
  {
@@ -2312,11 +2312,11 @@
2312
2312
  "--hover-low": "color-mix(in srgb, var(--text-primary) 5%, transparent)",
2313
2313
  "--hover": "color-mix(in srgb, var(--text-primary) 10%, transparent)",
2314
2314
  "--hover-high": "color-mix(in srgb, var(--text-primary) 15%, transparent)",
2315
- "--shadow-sm": "1px 1px 2px 0px hsla(237, 18%, 3%, 0.9)",
2316
- "--shadow-md": "3px 3px 6px 0px hsla(237, 18%, 3%, 0.9)",
2317
- "--shadow-lg": "5px 5px 9px 0px hsla(237, 18%, 3%, 0.9)",
2318
- "--shadow-xl": "6px 7px 13px 0px hsla(237, 18%, 3%, 0.9)",
2319
- "--shadow-2xl": "8px 9px 16px 0px hsla(237, 18%, 3%, 0.9)"
2315
+ "--shadow-sm": "1px 1px 2px hsla(237, 18%, 3%, 0.9)",
2316
+ "--shadow-md": "3px 3px 6px hsla(237, 18%, 3%, 0.9)",
2317
+ "--shadow-lg": "5px 5px 9px hsla(237, 18%, 3%, 0.9)",
2318
+ "--shadow-xl": "6px 7px 13px hsla(237, 18%, 3%, 0.9)",
2319
+ "--shadow-2xl": "8px 9px 16px hsla(237, 18%, 3%, 0.9)"
2320
2320
  },
2321
2321
  "fontSources": [
2322
2322
  {
@@ -2312,11 +2312,11 @@
2312
2312
  "--hover-low": "color-mix(in srgb, var(--text-primary) 5%, transparent)",
2313
2313
  "--hover": "color-mix(in srgb, var(--text-primary) 10%, transparent)",
2314
2314
  "--hover-high": "color-mix(in srgb, var(--text-primary) 15%, transparent)",
2315
- "--shadow-sm": "1px 1px 2px 0px hsla(237, 18%, 3%, 0.9)",
2316
- "--shadow-md": "3px 3px 6px 0px hsla(237, 18%, 3%, 0.9)",
2317
- "--shadow-lg": "5px 5px 9px 0px hsla(237, 18%, 3%, 0.9)",
2318
- "--shadow-xl": "6px 7px 13px 0px hsla(237, 18%, 3%, 0.9)",
2319
- "--shadow-2xl": "8px 9px 16px 0px hsla(237, 18%, 3%, 0.9)"
2315
+ "--shadow-sm": "1px 1px 2px hsla(237, 18%, 3%, 0.9)",
2316
+ "--shadow-md": "3px 3px 6px hsla(237, 18%, 3%, 0.9)",
2317
+ "--shadow-lg": "5px 5px 9px hsla(237, 18%, 3%, 0.9)",
2318
+ "--shadow-xl": "6px 7px 13px hsla(237, 18%, 3%, 0.9)",
2319
+ "--shadow-2xl": "8px 9px 16px hsla(237, 18%, 3%, 0.9)"
2320
2320
  },
2321
2321
  "fontSources": [
2322
2322
  {
@@ -2265,11 +2265,11 @@
2265
2265
  "--hover-low": "color-mix(in srgb, var(--text-primary) 5%, transparent)",
2266
2266
  "--hover": "color-mix(in srgb, var(--text-primary) 10%, transparent)",
2267
2267
  "--hover-high": "color-mix(in srgb, var(--text-primary) 15%, transparent)",
2268
- "--shadow-sm": "1px 1px 2px 0px hsla(237, 18%, 3%, 0.2)",
2269
- "--shadow-md": "3px 3px 6px 0px hsla(237, 18%, 3%, 0.2)",
2270
- "--shadow-lg": "5px 5px 9px 0px hsla(237, 18%, 3%, 0.2)",
2271
- "--shadow-xl": "6px 7px 13px 0px hsla(237, 18%, 3%, 0.2)",
2272
- "--shadow-2xl": "8px 9px 16px 0px hsla(237, 18%, 3%, 0.2)"
2268
+ "--shadow-sm": "1px 1px 2px hsla(237, 18%, 3%, 0.2)",
2269
+ "--shadow-md": "3px 3px 6px hsla(237, 18%, 3%, 0.2)",
2270
+ "--shadow-lg": "5px 5px 9px hsla(237, 18%, 3%, 0.2)",
2271
+ "--shadow-xl": "6px 7px 13px hsla(237, 18%, 3%, 0.2)",
2272
+ "--shadow-2xl": "8px 9px 16px hsla(237, 18%, 3%, 0.2)"
2273
2273
  },
2274
2274
  "fontSources": [
2275
2275
  {
@@ -2297,11 +2297,11 @@
2297
2297
  "--hover-low": "color-mix(in srgb, var(--text-primary) 5%, transparent)",
2298
2298
  "--hover": "color-mix(in srgb, var(--text-primary) 10%, transparent)",
2299
2299
  "--hover-high": "color-mix(in srgb, var(--text-primary) 15%, transparent)",
2300
- "--shadow-sm": "1px 1px 2px 0px hsla(237, 18%, 3%, 0.9)",
2301
- "--shadow-md": "3px 3px 6px 0px hsla(237, 18%, 3%, 0.9)",
2302
- "--shadow-lg": "5px 5px 9px 0px hsla(237, 18%, 3%, 0.9)",
2303
- "--shadow-xl": "6px 7px 13px 0px hsla(237, 18%, 3%, 0.9)",
2304
- "--shadow-2xl": "8px 9px 16px 0px hsla(237, 18%, 3%, 0.9)"
2300
+ "--shadow-sm": "1px 1px 2px hsla(237, 18%, 3%, 0.9)",
2301
+ "--shadow-md": "3px 3px 6px hsla(237, 18%, 3%, 0.9)",
2302
+ "--shadow-lg": "5px 5px 9px hsla(237, 18%, 3%, 0.9)",
2303
+ "--shadow-xl": "6px 7px 13px hsla(237, 18%, 3%, 0.9)",
2304
+ "--shadow-2xl": "8px 9px 16px hsla(237, 18%, 3%, 0.9)"
2305
2305
  },
2306
2306
  "fontSources": [
2307
2307
  {
@@ -2251,11 +2251,11 @@
2251
2251
  "--hover-low": "color-mix(in srgb, var(--text-primary) 5%, transparent)",
2252
2252
  "--hover": "color-mix(in srgb, var(--text-primary) 10%, transparent)",
2253
2253
  "--hover-high": "color-mix(in srgb, var(--text-primary) 15%, transparent)",
2254
- "--shadow-sm": "1px 1px 2px 0px hsla(237, 18%, 3%, 0.2)",
2255
- "--shadow-md": "3px 3px 6px 0px hsla(237, 18%, 3%, 0.2)",
2256
- "--shadow-lg": "5px 5px 9px 0px hsla(237, 18%, 3%, 0.2)",
2257
- "--shadow-xl": "6px 7px 13px 0px hsla(237, 18%, 3%, 0.2)",
2258
- "--shadow-2xl": "8px 9px 16px 0px hsla(237, 18%, 3%, 0.2)"
2254
+ "--shadow-sm": "1px 1px 2px hsla(237, 18%, 3%, 0.2)",
2255
+ "--shadow-md": "3px 3px 6px hsla(237, 18%, 3%, 0.2)",
2256
+ "--shadow-lg": "5px 5px 9px hsla(237, 18%, 3%, 0.2)",
2257
+ "--shadow-xl": "6px 7px 13px hsla(237, 18%, 3%, 0.2)",
2258
+ "--shadow-2xl": "8px 9px 16px hsla(237, 18%, 3%, 0.2)"
2259
2259
  },
2260
2260
  "fontSources": [
2261
2261
  {
@@ -2251,11 +2251,11 @@
2251
2251
  "--hover-low": "color-mix(in srgb, var(--text-primary) 5%, transparent)",
2252
2252
  "--hover": "color-mix(in srgb, var(--text-primary) 10%, transparent)",
2253
2253
  "--hover-high": "color-mix(in srgb, var(--text-primary) 15%, transparent)",
2254
- "--shadow-sm": "1px 1px 2px 0px hsla(237, 18%, 3%, 0.2)",
2255
- "--shadow-md": "3px 3px 6px 0px hsla(237, 18%, 3%, 0.2)",
2256
- "--shadow-lg": "5px 5px 9px 0px hsla(237, 18%, 3%, 0.2)",
2257
- "--shadow-xl": "6px 7px 13px 0px hsla(237, 18%, 3%, 0.2)",
2258
- "--shadow-2xl": "8px 9px 16px 0px hsla(237, 18%, 3%, 0.2)"
2254
+ "--shadow-sm": "1px 1px 2px hsla(237, 18%, 3%, 0.2)",
2255
+ "--shadow-md": "3px 3px 6px hsla(237, 18%, 3%, 0.2)",
2256
+ "--shadow-lg": "5px 5px 9px hsla(237, 18%, 3%, 0.2)",
2257
+ "--shadow-xl": "6px 7px 13px hsla(237, 18%, 3%, 0.2)",
2258
+ "--shadow-2xl": "8px 9px 16px hsla(237, 18%, 3%, 0.2)"
2259
2259
  },
2260
2260
  "fontSources": [
2261
2261
  {
@@ -2312,11 +2312,11 @@
2312
2312
  "--hover-low": "color-mix(in srgb, var(--text-primary) 5%, transparent)",
2313
2313
  "--hover": "color-mix(in srgb, var(--text-primary) 10%, transparent)",
2314
2314
  "--hover-high": "color-mix(in srgb, var(--text-primary) 15%, transparent)",
2315
- "--shadow-sm": "1px 1px 2px 0px hsla(237, 18%, 3%, 0.9)",
2316
- "--shadow-md": "3px 3px 6px 0px hsla(237, 18%, 3%, 0.9)",
2317
- "--shadow-lg": "5px 5px 9px 0px hsla(237, 18%, 3%, 0.9)",
2318
- "--shadow-xl": "6px 7px 13px 0px hsla(237, 18%, 3%, 0.9)",
2319
- "--shadow-2xl": "8px 9px 16px 0px hsla(237, 18%, 3%, 0.9)"
2315
+ "--shadow-sm": "1px 1px 2px hsla(237, 18%, 3%, 0.9)",
2316
+ "--shadow-md": "3px 3px 6px hsla(237, 18%, 3%, 0.9)",
2317
+ "--shadow-lg": "5px 5px 9px hsla(237, 18%, 3%, 0.9)",
2318
+ "--shadow-xl": "6px 7px 13px hsla(237, 18%, 3%, 0.9)",
2319
+ "--shadow-2xl": "8px 9px 16px hsla(237, 18%, 3%, 0.9)"
2320
2320
  },
2321
2321
  "fontSources": [
2322
2322
  {
@@ -2270,11 +2270,11 @@
2270
2270
  "--hover-low": "color-mix(in srgb, var(--text-primary) 5%, transparent)",
2271
2271
  "--hover": "color-mix(in srgb, var(--text-primary) 10%, transparent)",
2272
2272
  "--hover-high": "color-mix(in srgb, var(--text-primary) 15%, transparent)",
2273
- "--shadow-sm": "1px 1px 2px 0px hsla(237, 18%, 3%, 0.25)",
2274
- "--shadow-md": "3px 3px 6px 0px hsla(237, 18%, 3%, 0.25)",
2275
- "--shadow-lg": "5px 5px 9px 0px hsla(237, 18%, 3%, 0.25)",
2276
- "--shadow-xl": "6px 7px 13px 0px hsla(237, 18%, 3%, 0.25)",
2277
- "--shadow-2xl": "8px 9px 16px 0px hsla(237, 18%, 3%, 0.25)"
2273
+ "--shadow-sm": "1px 1px 2px hsla(237, 18%, 3%, 0.25)",
2274
+ "--shadow-md": "3px 3px 6px hsla(237, 18%, 3%, 0.25)",
2275
+ "--shadow-lg": "5px 5px 9px hsla(237, 18%, 3%, 0.25)",
2276
+ "--shadow-xl": "6px 7px 13px hsla(237, 18%, 3%, 0.25)",
2277
+ "--shadow-2xl": "8px 9px 16px hsla(237, 18%, 3%, 0.25)"
2278
2278
  },
2279
2279
  "fontSources": [
2280
2280
  {
@@ -2317,11 +2317,11 @@
2317
2317
  "--hover-low": "color-mix(in srgb, var(--text-primary) 5%, transparent)",
2318
2318
  "--hover": "color-mix(in srgb, var(--text-primary) 10%, transparent)",
2319
2319
  "--hover-high": "color-mix(in srgb, var(--text-primary) 15%, transparent)",
2320
- "--shadow-sm": "1px 1px 2px 0px hsla(237, 18%, 3%, 0.9)",
2321
- "--shadow-md": "3px 3px 6px 0px hsla(237, 18%, 3%, 0.9)",
2322
- "--shadow-lg": "5px 5px 9px 0px hsla(237, 18%, 3%, 0.9)",
2323
- "--shadow-xl": "6px 7px 13px 0px hsla(237, 18%, 3%, 0.9)",
2324
- "--shadow-2xl": "8px 9px 16px 0px hsla(237, 18%, 3%, 0.9)"
2320
+ "--shadow-sm": "1px 1px 2px hsla(237, 18%, 3%, 0.9)",
2321
+ "--shadow-md": "3px 3px 6px hsla(237, 18%, 3%, 0.9)",
2322
+ "--shadow-lg": "5px 5px 9px hsla(237, 18%, 3%, 0.9)",
2323
+ "--shadow-xl": "6px 7px 13px hsla(237, 18%, 3%, 0.9)",
2324
+ "--shadow-2xl": "8px 9px 16px hsla(237, 18%, 3%, 0.9)"
2325
2325
  },
2326
2326
  "fontSources": [
2327
2327
  {
@@ -2317,11 +2317,11 @@
2317
2317
  "--hover-low": "color-mix(in srgb, var(--text-primary) 5%, transparent)",
2318
2318
  "--hover": "color-mix(in srgb, var(--text-primary) 10%, transparent)",
2319
2319
  "--hover-high": "color-mix(in srgb, var(--text-primary) 15%, transparent)",
2320
- "--shadow-sm": "1px 1px 2px 0px hsla(237, 18%, 3%, 0.9)",
2321
- "--shadow-md": "3px 3px 6px 0px hsla(237, 18%, 3%, 0.9)",
2322
- "--shadow-lg": "5px 5px 9px 0px hsla(237, 18%, 3%, 0.9)",
2323
- "--shadow-xl": "6px 7px 13px 0px hsla(237, 18%, 3%, 0.9)",
2324
- "--shadow-2xl": "8px 9px 16px 0px hsla(237, 18%, 3%, 0.9)"
2320
+ "--shadow-sm": "1px 1px 2px hsla(237, 18%, 3%, 0.9)",
2321
+ "--shadow-md": "3px 3px 6px hsla(237, 18%, 3%, 0.9)",
2322
+ "--shadow-lg": "5px 5px 9px hsla(237, 18%, 3%, 0.9)",
2323
+ "--shadow-xl": "6px 7px 13px hsla(237, 18%, 3%, 0.9)",
2324
+ "--shadow-2xl": "8px 9px 16px hsla(237, 18%, 3%, 0.9)"
2325
2325
  },
2326
2326
  "fontSources": [
2327
2327
  {
@@ -2270,11 +2270,11 @@
2270
2270
  "--hover-low": "color-mix(in srgb, var(--text-primary) 5%, transparent)",
2271
2271
  "--hover": "color-mix(in srgb, var(--text-primary) 10%, transparent)",
2272
2272
  "--hover-high": "color-mix(in srgb, var(--text-primary) 15%, transparent)",
2273
- "--shadow-sm": "1px 1px 2px 0px hsla(237, 18%, 3%, 0.2)",
2274
- "--shadow-md": "3px 3px 6px 0px hsla(237, 18%, 3%, 0.2)",
2275
- "--shadow-lg": "5px 5px 9px 0px hsla(237, 18%, 3%, 0.2)",
2276
- "--shadow-xl": "6px 7px 13px 0px hsla(237, 18%, 3%, 0.2)",
2277
- "--shadow-2xl": "8px 9px 16px 0px hsla(237, 18%, 3%, 0.2)"
2273
+ "--shadow-sm": "1px 1px 2px hsla(237, 18%, 3%, 0.2)",
2274
+ "--shadow-md": "3px 3px 6px hsla(237, 18%, 3%, 0.2)",
2275
+ "--shadow-lg": "5px 5px 9px hsla(237, 18%, 3%, 0.2)",
2276
+ "--shadow-xl": "6px 7px 13px hsla(237, 18%, 3%, 0.2)",
2277
+ "--shadow-2xl": "8px 9px 16px hsla(237, 18%, 3%, 0.2)"
2278
2278
  },
2279
2279
  "fontSources": [
2280
2280
  {
@@ -2302,11 +2302,11 @@
2302
2302
  "--hover-low": "color-mix(in srgb, var(--text-primary) 5%, transparent)",
2303
2303
  "--hover": "color-mix(in srgb, var(--text-primary) 10%, transparent)",
2304
2304
  "--hover-high": "color-mix(in srgb, var(--text-primary) 15%, transparent)",
2305
- "--shadow-sm": "1px 1px 2px 0px hsla(237, 18%, 3%, 0.9)",
2306
- "--shadow-md": "3px 3px 6px 0px hsla(237, 18%, 3%, 0.9)",
2307
- "--shadow-lg": "5px 5px 9px 0px hsla(237, 18%, 3%, 0.9)",
2308
- "--shadow-xl": "6px 7px 13px 0px hsla(237, 18%, 3%, 0.9)",
2309
- "--shadow-2xl": "8px 9px 16px 0px hsla(237, 18%, 3%, 0.9)"
2305
+ "--shadow-sm": "1px 1px 2px hsla(237, 18%, 3%, 0.9)",
2306
+ "--shadow-md": "3px 3px 6px hsla(237, 18%, 3%, 0.9)",
2307
+ "--shadow-lg": "5px 5px 9px hsla(237, 18%, 3%, 0.9)",
2308
+ "--shadow-xl": "6px 7px 13px hsla(237, 18%, 3%, 0.9)",
2309
+ "--shadow-2xl": "8px 9px 16px hsla(237, 18%, 3%, 0.9)"
2310
2310
  },
2311
2311
  "fontSources": [
2312
2312
  {
@@ -2256,11 +2256,11 @@
2256
2256
  "--hover-low": "color-mix(in srgb, var(--text-primary) 5%, transparent)",
2257
2257
  "--hover": "color-mix(in srgb, var(--text-primary) 10%, transparent)",
2258
2258
  "--hover-high": "color-mix(in srgb, var(--text-primary) 15%, transparent)",
2259
- "--shadow-sm": "1px 1px 2px 0px hsla(237, 18%, 3%, 0.2)",
2260
- "--shadow-md": "3px 3px 6px 0px hsla(237, 18%, 3%, 0.2)",
2261
- "--shadow-lg": "5px 5px 9px 0px hsla(237, 18%, 3%, 0.2)",
2262
- "--shadow-xl": "6px 7px 13px 0px hsla(237, 18%, 3%, 0.2)",
2263
- "--shadow-2xl": "8px 9px 16px 0px hsla(237, 18%, 3%, 0.2)"
2259
+ "--shadow-sm": "1px 1px 2px hsla(237, 18%, 3%, 0.2)",
2260
+ "--shadow-md": "3px 3px 6px hsla(237, 18%, 3%, 0.2)",
2261
+ "--shadow-lg": "5px 5px 9px hsla(237, 18%, 3%, 0.2)",
2262
+ "--shadow-xl": "6px 7px 13px hsla(237, 18%, 3%, 0.2)",
2263
+ "--shadow-2xl": "8px 9px 16px hsla(237, 18%, 3%, 0.2)"
2264
2264
  },
2265
2265
  "fontSources": [
2266
2266
  {
@@ -2256,11 +2256,11 @@
2256
2256
  "--hover-low": "color-mix(in srgb, var(--text-primary) 5%, transparent)",
2257
2257
  "--hover": "color-mix(in srgb, var(--text-primary) 10%, transparent)",
2258
2258
  "--hover-high": "color-mix(in srgb, var(--text-primary) 15%, transparent)",
2259
- "--shadow-sm": "1px 1px 2px 0px hsla(237, 18%, 3%, 0.2)",
2260
- "--shadow-md": "3px 3px 6px 0px hsla(237, 18%, 3%, 0.2)",
2261
- "--shadow-lg": "5px 5px 9px 0px hsla(237, 18%, 3%, 0.2)",
2262
- "--shadow-xl": "6px 7px 13px 0px hsla(237, 18%, 3%, 0.2)",
2263
- "--shadow-2xl": "8px 9px 16px 0px hsla(237, 18%, 3%, 0.2)"
2259
+ "--shadow-sm": "1px 1px 2px hsla(237, 18%, 3%, 0.2)",
2260
+ "--shadow-md": "3px 3px 6px hsla(237, 18%, 3%, 0.2)",
2261
+ "--shadow-lg": "5px 5px 9px hsla(237, 18%, 3%, 0.2)",
2262
+ "--shadow-xl": "6px 7px 13px hsla(237, 18%, 3%, 0.2)",
2263
+ "--shadow-2xl": "8px 9px 16px hsla(237, 18%, 3%, 0.2)"
2264
2264
  },
2265
2265
  "fontSources": [
2266
2266
  {
@@ -2317,11 +2317,11 @@
2317
2317
  "--hover-low": "color-mix(in srgb, var(--text-primary) 5%, transparent)",
2318
2318
  "--hover": "color-mix(in srgb, var(--text-primary) 10%, transparent)",
2319
2319
  "--hover-high": "color-mix(in srgb, var(--text-primary) 15%, transparent)",
2320
- "--shadow-sm": "1px 1px 2px 0px hsla(237, 18%, 3%, 0.9)",
2321
- "--shadow-md": "3px 3px 6px 0px hsla(237, 18%, 3%, 0.9)",
2322
- "--shadow-lg": "5px 5px 9px 0px hsla(237, 18%, 3%, 0.9)",
2323
- "--shadow-xl": "6px 7px 13px 0px hsla(237, 18%, 3%, 0.9)",
2324
- "--shadow-2xl": "8px 9px 16px 0px hsla(237, 18%, 3%, 0.9)"
2320
+ "--shadow-sm": "1px 1px 2px hsla(237, 18%, 3%, 0.9)",
2321
+ "--shadow-md": "3px 3px 6px hsla(237, 18%, 3%, 0.9)",
2322
+ "--shadow-lg": "5px 5px 9px hsla(237, 18%, 3%, 0.9)",
2323
+ "--shadow-xl": "6px 7px 13px hsla(237, 18%, 3%, 0.9)",
2324
+ "--shadow-2xl": "8px 9px 16px hsla(237, 18%, 3%, 0.9)"
2325
2325
  },
2326
2326
  "fontSources": [
2327
2327
  {
@@ -53,11 +53,11 @@
53
53
  --hover-low: color-mix(in srgb, var(--text-primary) 5%, transparent);
54
54
  --hover: color-mix(in srgb, var(--text-primary) 10%, transparent);
55
55
  --hover-high: color-mix(in srgb, var(--text-primary) 15%, transparent);
56
- --shadow-sm: 1px 1px 2px 0px hsla(237, 18%, 3%, 0.9);
57
- --shadow-md: 3px 3px 6px 0px hsla(237, 18%, 3%, 0.9);
58
- --shadow-lg: 5px 5px 9px 0px hsla(237, 18%, 3%, 0.9);
59
- --shadow-xl: 6px 7px 13px 0px hsla(237, 18%, 3%, 0.9);
60
- --shadow-2xl: 8px 9px 16px 0px hsla(237, 18%, 3%, 0.9);
56
+ --shadow-sm: 1px 1px 2px hsla(237, 18%, 3%, 0.9);
57
+ --shadow-md: 3px 3px 6px hsla(237, 18%, 3%, 0.9);
58
+ --shadow-lg: 5px 5px 9px hsla(237, 18%, 3%, 0.9);
59
+ --shadow-xl: 6px 7px 13px hsla(237, 18%, 3%, 0.9);
60
+ --shadow-2xl: 8px 9px 16px hsla(237, 18%, 3%, 0.9);
61
61
  --color-brand-100: oklch(0.95 0.0279 355.35);
62
62
  --color-brand-200: oklch(0.8508 0.0915 355.35);
63
63
  --color-brand-300: oklch(0.7516 0.1697 355.35);
@@ -24,6 +24,13 @@
24
24
  `cover` only crops when the thumbnail has its own box (an aspect from
25
25
  `width`/`height`, or a CSS-constrained container). */
26
26
  fit?: 'contain' | 'cover';
27
+ /** Where the tile's shadow lands. Both modes read
28
+ `--imagelightbox-tile-shadow`: `box` casts it from the tile's rectangle,
29
+ `content` casts it from the image's own alpha, so art with transparency
30
+ doesn't sit on a phantom rectangle. `none` drops it. A theme that dials
31
+ a non-zero spread has no shadow under `content` — `drop-shadow()` has no
32
+ spread slot. */
33
+ shadow?: 'box' | 'content' | 'none';
27
34
  /** When true, shows a bottom toolbar (zoom in/out + percent) and a top-right close button, and enables wheel/drag zoom inside the open modal. When false, click anywhere closes. */
28
35
  extended?: boolean;
29
36
  /** Maximum zoom, as a multiple of the image's natural resolution: `1` = 100%
@@ -55,6 +62,7 @@
55
62
  images = undefined,
56
63
  maxWidth = undefined,
57
64
  fit = 'contain',
65
+ shadow = 'box',
58
66
  extended = false,
59
67
  maxZoom = undefined,
60
68
  capNatural = false,
@@ -582,6 +590,8 @@
582
590
  <button
583
591
  bind:this={thumbEl}
584
592
  class="image-lightbox-thumb"
593
+ class:shadow-box={shadow === 'box'}
594
+ class:shadow-content={shadow === 'content'}
585
595
  style:--imagelightbox-tile-object-fit={fit}
586
596
  type="button"
587
597
  aria-label={cover?.alt ? `Expand image: ${cover.alt}` : 'Expand image'}
@@ -618,6 +628,8 @@
618
628
  <div
619
629
  bind:this={stageEl}
620
630
  class="image-lightbox-stage"
631
+ class:shadow-box={shadow === 'box'}
632
+ class:shadow-content={shadow === 'content'}
621
633
  role="presentation"
622
634
  tabindex="-1"
623
635
  onclick={onStageClick}
@@ -747,12 +759,25 @@
747
759
  cursor: zoom-in;
748
760
  border: var(--imagelightbox-tile-border-width) solid var(--imagelightbox-tile-border);
749
761
  border-radius: var(--imagelightbox-tile-radius);
750
- box-shadow: var(--imagelightbox-tile-shadow);
751
762
  background: transparent;
752
763
  overflow: hidden;
753
764
  transition: transform 250ms ease;
754
765
  }
755
766
 
767
+ .image-lightbox-thumb.shadow-box,
768
+ .image-lightbox-stage.shadow-box {
769
+ box-shadow: var(--imagelightbox-tile-shadow);
770
+ }
771
+
772
+ /* Content mode traces the image's alpha. The filter goes on the tile, not the
773
+ <img>: applied to the element it runs after that element's own overflow
774
+ clip, so the shadow lands outside the box instead of being cropped by it.
775
+ The open modal stays bare — it casts against a near-opaque scrim where no
776
+ shadow reads, and a filter over the pan/zoom surface repaints every frame. */
777
+ .image-lightbox-thumb.shadow-content {
778
+ filter: drop-shadow(var(--imagelightbox-tile-shadow));
779
+ }
780
+
756
781
  .image-lightbox-thumb:hover {
757
782
  transform: scale(1.02);
758
783
  }
@@ -793,7 +818,6 @@
793
818
  cursor: zoom-out;
794
819
  border: var(--imagelightbox-tile-border-width) solid var(--imagelightbox-tile-border);
795
820
  border-radius: var(--imagelightbox-tile-radius);
796
- box-shadow: var(--imagelightbox-tile-shadow);
797
821
  background: transparent;
798
822
  }
799
823
 
@@ -530,11 +530,11 @@
530
530
 
531
531
  /* Shadows (elevation scale) */
532
532
  --shadow-none: none;
533
- --shadow-sm: 1px 1px 2px 0px hsla(237, 18%, 3%, 0.9);
534
- --shadow-md: 3px 3px 6px 0px hsla(237, 18%, 3%, 0.9);
535
- --shadow-lg: 5px 5px 9px 0px hsla(237, 18%, 3%, 0.9);
536
- --shadow-xl: 6px 7px 13px 0px hsla(237, 18%, 3%, 0.9);
537
- --shadow-2xl: 8px 9px 16px 0px hsla(237, 18%, 3%, 0.9);
533
+ --shadow-sm: 1px 1px 2px hsla(237, 18%, 3%, 0.9);
534
+ --shadow-md: 3px 3px 6px hsla(237, 18%, 3%, 0.9);
535
+ --shadow-lg: 5px 5px 9px hsla(237, 18%, 3%, 0.9);
536
+ --shadow-xl: 6px 7px 13px hsla(237, 18%, 3%, 0.9);
537
+ --shadow-2xl: 8px 9px 16px hsla(237, 18%, 3%, 0.9);
538
538
 
539
539
  /* Focus rings (box-shadow outline) */
540
540
  --ring-focus-sm: 0 0 0 2px hsla(0, 78%, 60%, 0.1);