@zylem/ui 0.8.9 → 0.8.11

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 (51) hide show
  1. package/dist/styles.css +1 -1
  2. package/package.json +12 -10
  3. package/src/components/Accordion/Accordion.tsx +10 -1
  4. package/src/components/AddTile/AddTile.css.ts +17 -0
  5. package/src/components/AddTile/AddTile.tsx +15 -7
  6. package/src/components/Callout/Callout.css.ts +45 -0
  7. package/src/components/Callout/Callout.tsx +32 -0
  8. package/src/components/Card/Card.css.ts +18 -0
  9. package/src/components/Card/Card.tsx +25 -2
  10. package/src/components/Dialog/Dialog.css.ts +69 -2
  11. package/src/components/Dialog/Dialog.tsx +35 -7
  12. package/src/components/DropdownMenu/DropdownMenu.css.ts +22 -0
  13. package/src/components/DropdownMenu/DropdownMenu.tsx +67 -20
  14. package/src/components/EmptyState/EmptyState.css.ts +9 -0
  15. package/src/components/EmptyState/EmptyState.tsx +3 -1
  16. package/src/components/FrameStrip/FrameStrip.css.ts +6 -0
  17. package/src/components/FrameStrip/FrameStrip.tsx +30 -2
  18. package/src/components/Menubar/Menubar.css.ts +52 -0
  19. package/src/components/Menubar/Menubar.tsx +77 -0
  20. package/src/components/Mixer/Knob.tsx +188 -0
  21. package/src/components/Mixer/Mixer.css.ts +246 -0
  22. package/src/components/Mixer/MixerStrip.tsx +150 -0
  23. package/src/components/NumberField/NumberField.tsx +5 -1
  24. package/src/components/Panel/Panel.tsx +6 -5
  25. package/src/components/PianoRoll/PianoRoll.css.ts +224 -0
  26. package/src/components/PianoRoll/PianoRoll.tsx +353 -0
  27. package/src/components/PianoRoll/piano-roll-math.ts +89 -0
  28. package/src/components/Property/Property.css.ts +1 -1
  29. package/src/components/Property/Property.tsx +3 -0
  30. package/src/components/Sidebar/Sidebar.css.ts +8 -0
  31. package/src/components/Sidebar/Sidebar.tsx +10 -2
  32. package/src/components/Slider/Slider.tsx +6 -0
  33. package/src/components/SpriteSizePicker/SpriteSizePicker.tsx +15 -4
  34. package/src/components/Tabs/Tabs.css.ts +48 -0
  35. package/src/components/Tabs/Tabs.tsx +41 -3
  36. package/src/components/TextField/TextField.tsx +49 -5
  37. package/src/components/TimelineLanes/TimelineLanes.css.ts +365 -0
  38. package/src/components/TimelineLanes/TimelineLanes.tsx +509 -0
  39. package/src/components/TimelineLanes/lanes-math.ts +182 -0
  40. package/src/components/Toast/Toast.css.ts +48 -0
  41. package/src/components/Toast/Toast.tsx +37 -0
  42. package/src/components/TrackHeader/TrackHeader.css.ts +162 -0
  43. package/src/components/TrackHeader/TrackHeader.tsx +145 -0
  44. package/src/components/TransportBar/TransportBar.css.ts +65 -0
  45. package/src/components/TransportBar/TransportBar.tsx +154 -0
  46. package/src/components/Waveform/Waveform.css.ts +77 -0
  47. package/src/components/Waveform/Waveform.tsx +195 -0
  48. package/src/components/Waveform/peaks.ts +49 -0
  49. package/src/components/index.ts +55 -1
  50. package/src/global/accordion.css.ts +23 -0
  51. package/src/styles.ts +11 -0
@@ -1,4 +1,6 @@
1
1
  import { Tabs as KTabs } from '@kobalte/core';
2
+ import X from 'lucide-solid/icons/x';
3
+ import { Show } from 'solid-js';
2
4
  import type { JSX } from 'solid-js';
3
5
 
4
6
  export interface TabsProps {
@@ -38,12 +40,29 @@ export function TabsList(props: { children: JSX.Element; class?: string }) {
38
40
  );
39
41
  }
40
42
 
41
- export function TabsTrigger(props: {
43
+ export interface TabsTriggerProps {
42
44
  value: string;
43
45
  children: JSX.Element;
44
46
  disabled?: boolean;
45
47
  class?: string;
46
- }) {
48
+ /**
49
+ * Document-style tabs: shows a close affordance after the label. It is a
50
+ * `role="button"` span (a button may not nest in the trigger button) and
51
+ * closing does not select the tab.
52
+ */
53
+ onClose?: () => void;
54
+ /** Accessible name of the close affordance; defaults to `Close tab`. */
55
+ closeLabel?: string;
56
+ /** Unsaved-changes marker before the close affordance. */
57
+ dirty?: boolean;
58
+ }
59
+
60
+ export function TabsTrigger(props: TabsTriggerProps) {
61
+ const close = (event: Event) => {
62
+ event.stopPropagation();
63
+ event.preventDefault();
64
+ props.onClose?.();
65
+ };
47
66
  return (
48
67
  <KTabs.Trigger
49
68
  class={
@@ -52,7 +71,26 @@ export function TabsTrigger(props: {
52
71
  value={props.value}
53
72
  disabled={props.disabled}
54
73
  >
55
- {props.children}
74
+ <span class="zylem-tabs-trigger-label">{props.children}</span>
75
+ <Show when={props.dirty}>
76
+ <span class="zylem-tabs-dirty" aria-label="Unsaved changes" title="Unsaved changes" />
77
+ </Show>
78
+ <Show when={props.onClose}>
79
+ <span
80
+ class="zylem-tabs-close"
81
+ role="button"
82
+ tabIndex={0}
83
+ aria-label={props.closeLabel ?? 'Close tab'}
84
+ title={props.closeLabel ?? 'Close tab'}
85
+ onClick={close}
86
+ onPointerDown={(event) => event.stopPropagation()}
87
+ onKeyDown={(event) => {
88
+ if (event.key === 'Enter' || event.key === ' ') close(event);
89
+ }}
90
+ >
91
+ <X aria-hidden="true" />
92
+ </span>
93
+ </Show>
56
94
  </KTabs.Trigger>
57
95
  );
58
96
  }
@@ -1,5 +1,5 @@
1
1
  import { TextField as KTextField } from '@kobalte/core';
2
- import { Show, splitProps } from 'solid-js';
2
+ import { createEffect, createSignal, on, Show, splitProps } from 'solid-js';
3
3
  import type { JSX } from 'solid-js';
4
4
 
5
5
  export interface TextFieldProps {
@@ -7,8 +7,17 @@ export interface TextFieldProps {
7
7
  error?: string;
8
8
  placeholder?: string;
9
9
  value?: string;
10
+ /** Fires on every edit. */
10
11
  onChange?: (value: string) => void;
12
+ /**
13
+ * Fires on blur and, for single-line fields, on Enter. While set, the field
14
+ * edits a local draft and only reports it here, so a parent that normalises
15
+ * or rejects values (rename, comma lists, keys) does not fight the typing;
16
+ * the draft snaps back to `value` after each commit.
17
+ */
18
+ onCommit?: (value: string) => void;
11
19
  onInput?: JSX.EventHandlerUnion<HTMLInputElement | HTMLTextAreaElement, InputEvent>;
20
+ onBlur?: JSX.EventHandlerUnion<HTMLInputElement | HTMLTextAreaElement, FocusEvent>;
12
21
  type?: string;
13
22
  name?: string;
14
23
  disabled?: boolean;
@@ -21,18 +30,51 @@ export interface TextFieldProps {
21
30
  onKeyDown?: JSX.EventHandlerUnion<HTMLInputElement | HTMLTextAreaElement, KeyboardEvent>;
22
31
  }
23
32
 
33
+ function callHandler<T, E extends Event>(
34
+ handler: JSX.EventHandlerUnion<T, E> | undefined,
35
+ event: E & { currentTarget: T; target: Element },
36
+ ) {
37
+ if (!handler) return;
38
+ if (typeof handler === 'function') handler(event);
39
+ else handler[0](handler[1], event);
40
+ }
41
+
24
42
  /**
25
43
  * HyperGlass text field: inset glass input with an uppercase HyperCard-style
26
44
  * label. Set `multiline` for a textarea.
27
45
  */
28
46
  export function TextField(props: TextFieldProps) {
29
47
  const [local] = splitProps(props, ['label', 'error', 'multiline', 'class']);
48
+ const [draft, setDraft] = createSignal(props.value ?? '');
49
+ createEffect(on(() => props.value, (value) => setDraft(value ?? '')));
50
+
51
+ const value = () => (props.onCommit ? draft() : props.value);
52
+ const change = (next: string) => {
53
+ setDraft(next);
54
+ props.onChange?.(next);
55
+ };
56
+ const commit = () => {
57
+ if (!props.onCommit) return;
58
+ props.onCommit(draft());
59
+ setDraft(props.value ?? '');
60
+ };
61
+
62
+ const blur = (event: FocusEvent & { currentTarget: HTMLInputElement | HTMLTextAreaElement; target: Element }) => {
63
+ callHandler(props.onBlur, event);
64
+ commit();
65
+ };
66
+ const keyDown = (
67
+ event: KeyboardEvent & { currentTarget: HTMLInputElement | HTMLTextAreaElement; target: Element },
68
+ ) => {
69
+ callHandler(props.onKeyDown, event);
70
+ if (event.key === 'Enter' && !local.multiline && !event.defaultPrevented) commit();
71
+ };
30
72
 
31
73
  return (
32
74
  <KTextField.Root
33
75
  class={local.class ? `zylem-field-root ${local.class}` : 'zylem-field-root'}
34
- value={props.value}
35
- onChange={props.onChange}
76
+ value={value()}
77
+ onChange={change}
36
78
  name={props.name}
37
79
  disabled={props.disabled}
38
80
  readOnly={props.readOnly}
@@ -53,7 +95,8 @@ export function TextField(props: TextFieldProps) {
53
95
  placeholder={props.placeholder}
54
96
  autofocus={props.autofocus}
55
97
  onInput={props.onInput as JSX.EventHandlerUnion<HTMLInputElement, InputEvent>}
56
- onKeyDown={props.onKeyDown as JSX.EventHandlerUnion<HTMLInputElement, KeyboardEvent>}
98
+ onBlur={blur}
99
+ onKeyDown={keyDown}
57
100
  />
58
101
  }
59
102
  >
@@ -63,7 +106,8 @@ export function TextField(props: TextFieldProps) {
63
106
  rows={props.rows}
64
107
  autofocus={props.autofocus}
65
108
  onInput={props.onInput as JSX.EventHandlerUnion<HTMLTextAreaElement, InputEvent>}
66
- onKeyDown={props.onKeyDown as JSX.EventHandlerUnion<HTMLTextAreaElement, KeyboardEvent>}
109
+ onBlur={blur}
110
+ onKeyDown={keyDown}
67
111
  />
68
112
  </Show>
69
113
  <Show when={local.error}>
@@ -0,0 +1,365 @@
1
+ import { globalStyle } from '@vanilla-extract/css';
2
+ import { vars } from '../../theme.css';
3
+
4
+ const RULER_HEIGHT = '26px';
5
+ const LANE_BORDER = '1px solid rgba(97, 166, 232, 0.16)';
6
+
7
+ globalStyle('.zylem-lanes', {
8
+ position: 'relative',
9
+ overflow: 'auto',
10
+ minHeight: 0,
11
+ minWidth: 0,
12
+ fontFamily: vars.typography.fontFamily,
13
+ fontSize: `calc(${vars.typography.fontSize} - 2px)`,
14
+ color: vars.colors.text,
15
+ background: vars.material.fieldGlass,
16
+ border: '1px solid rgba(97, 166, 232, 0.22)',
17
+ borderRadius: vars.radii.control,
18
+ boxShadow: vars.effects.shadowInset,
19
+ userSelect: 'none',
20
+ });
21
+
22
+ globalStyle('.zylem-lanes[data-disabled]', {
23
+ opacity: 0.6,
24
+ });
25
+
26
+ globalStyle('.zylem-lanes-content', {
27
+ position: 'relative',
28
+ minWidth: '100%',
29
+ minHeight: '100%',
30
+ });
31
+
32
+ // Ruler row: sticky to the top of the scroller, above the lanes and playhead.
33
+ globalStyle('.zylem-lanes-ruler-row', {
34
+ position: 'sticky',
35
+ top: 0,
36
+ zIndex: 3,
37
+ display: 'flex',
38
+ height: RULER_HEIGHT,
39
+ background: vars.material.panelHeaderGloss,
40
+ backgroundColor: 'rgba(14, 30, 48, 0.96)',
41
+ borderBottom: '1px solid rgba(97, 166, 232, 0.3)',
42
+ boxShadow: '0 1px 0 rgba(255,255,255,0.06) inset',
43
+ });
44
+
45
+ globalStyle('.zylem-lanes-corner', {
46
+ position: 'sticky',
47
+ left: 0,
48
+ zIndex: 4,
49
+ flex: 'none',
50
+ width: 'var(--zylem-lanes-header)',
51
+ display: 'flex',
52
+ alignItems: 'center',
53
+ padding: `0 ${vars.spacing.sm}`,
54
+ fontSize: '10px',
55
+ letterSpacing: '0.08em',
56
+ textTransform: 'uppercase',
57
+ color: vars.colors.textSecondary,
58
+ background: 'rgba(14, 30, 48, 0.98)',
59
+ borderRight: LANE_BORDER,
60
+ });
61
+
62
+ globalStyle('.zylem-lanes-ruler', {
63
+ position: 'relative',
64
+ flex: 'none',
65
+ height: '100%',
66
+ cursor: 'pointer',
67
+ });
68
+
69
+ globalStyle('.zylem-lanes-range', {
70
+ position: 'absolute',
71
+ top: 0,
72
+ bottom: 0,
73
+ background: 'rgba(240, 180, 60, 0.18)',
74
+ borderLeft: '1px solid rgba(240, 180, 60, 0.7)',
75
+ borderRight: '1px solid rgba(240, 180, 60, 0.7)',
76
+ pointerEvents: 'none',
77
+ });
78
+
79
+ globalStyle('.zylem-lanes-tick', {
80
+ position: 'absolute',
81
+ bottom: 0,
82
+ width: '1px',
83
+ height: '6px',
84
+ background: 'rgba(205, 232, 255, 0.35)',
85
+ pointerEvents: 'none',
86
+ });
87
+
88
+ globalStyle('.zylem-lanes-tick[data-major]', {
89
+ height: '10px',
90
+ background: 'rgba(205, 232, 255, 0.7)',
91
+ });
92
+
93
+ globalStyle('.zylem-lanes-tick-label', {
94
+ position: 'absolute',
95
+ left: '4px',
96
+ bottom: '8px',
97
+ fontSize: '10px',
98
+ fontVariantNumeric: 'tabular-nums',
99
+ whiteSpace: 'nowrap',
100
+ color: vars.colors.textSecondary,
101
+ });
102
+
103
+ globalStyle('.zylem-lanes-playhead-head', {
104
+ position: 'absolute',
105
+ bottom: 0,
106
+ width: 0,
107
+ height: 0,
108
+ marginLeft: '-6px',
109
+ borderLeft: '6px solid transparent',
110
+ borderRight: '6px solid transparent',
111
+ borderTop: '8px solid rgba(255, 244, 200, 0.98)',
112
+ filter: 'drop-shadow(0 0 3px rgba(240, 180, 60, 0.8))',
113
+ pointerEvents: 'none',
114
+ });
115
+
116
+ globalStyle('.zylem-lanes-playhead', {
117
+ position: 'absolute',
118
+ top: RULER_HEIGHT,
119
+ bottom: 0,
120
+ zIndex: 1,
121
+ width: '1px',
122
+ marginLeft: '-0.5px',
123
+ background: 'rgba(255, 236, 170, 0.9)',
124
+ boxShadow: '0 0 6px rgba(240, 180, 60, 0.7)',
125
+ pointerEvents: 'none',
126
+ });
127
+
128
+ globalStyle('.zylem-lanes-rows', {
129
+ display: 'flex',
130
+ flexDirection: 'column',
131
+ });
132
+
133
+ // Lanes: sticky header cell + a body that carries the beat/second grid.
134
+ globalStyle('.zylem-lane', {
135
+ display: 'flex',
136
+ flex: 'none',
137
+ borderBottom: LANE_BORDER,
138
+ });
139
+
140
+ globalStyle('.zylem-lane[data-selected]', {
141
+ background: 'rgba(97, 166, 232, 0.07)',
142
+ });
143
+
144
+ globalStyle('.zylem-lane-header', {
145
+ position: 'sticky',
146
+ left: 0,
147
+ zIndex: 2,
148
+ flex: 'none',
149
+ width: 'var(--zylem-lanes-header)',
150
+ minWidth: 0,
151
+ display: 'flex',
152
+ alignItems: 'stretch',
153
+ background: 'rgba(16, 27, 42, 0.96)',
154
+ borderRight: LANE_BORDER,
155
+ });
156
+
157
+ globalStyle('.zylem-lane[data-selected] > .zylem-lane-header', {
158
+ background: 'rgba(30, 54, 82, 0.96)',
159
+ });
160
+
161
+ globalStyle('.zylem-lane-body', {
162
+ position: 'relative',
163
+ flex: 'none',
164
+ height: '100%',
165
+ backgroundImage: [
166
+ 'repeating-linear-gradient(90deg, rgba(205, 232, 255, 0.16) 0 1px, transparent 1px var(--zylem-lanes-major))',
167
+ 'repeating-linear-gradient(90deg, rgba(205, 232, 255, 0.06) 0 1px, transparent 1px var(--zylem-lanes-minor))',
168
+ ].join(', '),
169
+ });
170
+
171
+ globalStyle('.zylem-lane[data-dimmed] > .zylem-lane-body', {
172
+ opacity: 0.45,
173
+ });
174
+
175
+ globalStyle('.zylem-lane--group', {
176
+ background: 'rgba(97, 166, 232, 0.08)',
177
+ });
178
+
179
+ globalStyle('.zylem-lane--group > .zylem-lane-header', {
180
+ fontSize: '10px',
181
+ letterSpacing: '0.08em',
182
+ textTransform: 'uppercase',
183
+ color: vars.colors.textSecondary,
184
+ });
185
+
186
+ globalStyle('.zylem-lane--group > .zylem-lane-body', {
187
+ backgroundImage: 'none',
188
+ });
189
+
190
+ globalStyle('.zylem-lane-end', {
191
+ position: 'absolute',
192
+ top: 0,
193
+ bottom: 0,
194
+ right: 0,
195
+ background: 'rgba(0, 0, 0, 0.28)',
196
+ pointerEvents: 'none',
197
+ });
198
+
199
+ // Clips: color-tinted glass blocks with edge handles.
200
+ globalStyle('.zylem-clip', {
201
+ position: 'absolute',
202
+ top: '4px',
203
+ bottom: '4px',
204
+ boxSizing: 'border-box',
205
+ display: 'flex',
206
+ flexDirection: 'column',
207
+ overflow: 'hidden',
208
+ borderRadius: '5px',
209
+ border: '1px solid color-mix(in srgb, var(--zylem-clip-color) 70%, white 30%)',
210
+ background:
211
+ 'linear-gradient(180deg, color-mix(in srgb, var(--zylem-clip-color) 70%, white 30%) 0%, color-mix(in srgb, var(--zylem-clip-color) 75%, black 25%) 100%)',
212
+ boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.35), 0 1px 3px rgba(0,0,0,0.45)',
213
+ color: '#0b1522',
214
+ cursor: 'grab',
215
+ outline: 'none',
216
+ transition: `box-shadow ${vars.motion.fast} ${vars.motion.easeOut}, filter ${vars.motion.fast} ${vars.motion.easeOut}`,
217
+ });
218
+
219
+ globalStyle('.zylem-clip:hover:not([data-disabled])', {
220
+ filter: 'brightness(1.08)',
221
+ });
222
+
223
+ globalStyle('.zylem-clip[data-selected]', {
224
+ borderColor: 'rgba(255, 255, 255, 0.9)',
225
+ boxShadow: `inset 0 1px 0 rgba(255,255,255,0.45), 0 0 0 1px rgba(255,255,255,0.5), ${vars.effects.glowPrimary}`,
226
+ });
227
+
228
+ globalStyle('.zylem-clip:focus-visible', {
229
+ boxShadow: vars.effects.focusRing,
230
+ });
231
+
232
+ globalStyle('.zylem-clip[data-dragging]', {
233
+ cursor: 'grabbing',
234
+ filter: 'brightness(1.12)',
235
+ });
236
+
237
+ globalStyle('.zylem-clip[data-disabled]', {
238
+ cursor: 'default',
239
+ opacity: 0.7,
240
+ });
241
+
242
+ globalStyle('.zylem-clip-label', {
243
+ flex: 'none',
244
+ padding: '2px 6px 0',
245
+ fontSize: '11px',
246
+ fontWeight: 600,
247
+ lineHeight: '14px',
248
+ whiteSpace: 'nowrap',
249
+ overflow: 'hidden',
250
+ textOverflow: 'ellipsis',
251
+ textShadow: '0 1px 0 rgba(255,255,255,0.25)',
252
+ });
253
+
254
+ globalStyle('.zylem-clip-body', {
255
+ position: 'relative',
256
+ flex: '1 1 auto',
257
+ minHeight: 0,
258
+ overflow: 'hidden',
259
+ pointerEvents: 'none',
260
+ });
261
+
262
+ globalStyle('.zylem-clip-blend', {
263
+ position: 'absolute',
264
+ top: 0,
265
+ bottom: 0,
266
+ left: 0,
267
+ background:
268
+ 'repeating-linear-gradient(135deg, rgba(0,0,0,0.28) 0 3px, transparent 3px 7px)',
269
+ borderRight: '1px dashed rgba(0,0,0,0.45)',
270
+ pointerEvents: 'none',
271
+ });
272
+
273
+ globalStyle('.zylem-clip-handle', {
274
+ position: 'absolute',
275
+ top: 0,
276
+ bottom: 0,
277
+ width: '7px',
278
+ cursor: 'ew-resize',
279
+ });
280
+
281
+ globalStyle('.zylem-clip-handle[data-edge="start"]', { left: 0 });
282
+ globalStyle('.zylem-clip-handle[data-edge="end"]', { right: 0 });
283
+
284
+ globalStyle('.zylem-clip-handle:hover', {
285
+ background: 'rgba(255,255,255,0.28)',
286
+ });
287
+
288
+ // Markers: diamonds (keyframes) and pins (events) centred on a time.
289
+ globalStyle('.zylem-marker', {
290
+ position: 'absolute',
291
+ top: '50%',
292
+ display: 'flex',
293
+ alignItems: 'center',
294
+ gap: '4px',
295
+ padding: 0,
296
+ margin: 0,
297
+ border: 0,
298
+ background: 'transparent',
299
+ color: vars.colors.text,
300
+ cursor: 'grab',
301
+ transform: 'translate(-50%, -50%)',
302
+ outline: 'none',
303
+ });
304
+
305
+ globalStyle('.zylem-marker[data-shape="pin"]', {
306
+ transform: 'translate(-7px, -50%)',
307
+ });
308
+
309
+ globalStyle('.zylem-marker:disabled', {
310
+ cursor: 'default',
311
+ opacity: 0.6,
312
+ });
313
+
314
+ globalStyle('.zylem-marker[data-dragging]', {
315
+ cursor: 'grabbing',
316
+ });
317
+
318
+ globalStyle('.zylem-marker-glyph', {
319
+ display: 'block',
320
+ width: '11px',
321
+ height: '11px',
322
+ boxSizing: 'border-box',
323
+ border: '1px solid rgba(255, 255, 255, 0.7)',
324
+ boxShadow: '0 1px 3px rgba(0,0,0,0.55)',
325
+ transition: `box-shadow ${vars.motion.fast} ${vars.motion.easeOut}, filter ${vars.motion.fast} ${vars.motion.easeOut}`,
326
+ });
327
+
328
+ globalStyle('.zylem-marker[data-shape="diamond"] .zylem-marker-glyph', {
329
+ borderRadius: '2px',
330
+ background:
331
+ 'var(--zylem-marker-color, linear-gradient(135deg, rgba(255, 244, 200, 0.98), rgba(240, 180, 60, 0.95)))',
332
+ transform: 'rotate(45deg)',
333
+ });
334
+
335
+ globalStyle('.zylem-marker[data-shape="pin"] .zylem-marker-glyph', {
336
+ width: '13px',
337
+ height: '13px',
338
+ borderRadius: '999px 999px 999px 2px',
339
+ background:
340
+ 'var(--zylem-marker-color, linear-gradient(135deg, rgba(178, 219, 255, 0.98), rgba(48, 137, 220, 0.95)))',
341
+ transform: 'rotate(-45deg)',
342
+ });
343
+
344
+ globalStyle('.zylem-marker:hover:not(:disabled) .zylem-marker-glyph', {
345
+ filter: 'brightness(1.12)',
346
+ });
347
+
348
+ globalStyle('.zylem-marker[data-selected] .zylem-marker-glyph', {
349
+ borderColor: 'white',
350
+ boxShadow: '0 1px 3px rgba(0,0,0,0.55), 0 0 10px rgba(255, 255, 255, 0.7)',
351
+ });
352
+
353
+ globalStyle('.zylem-marker:focus-visible .zylem-marker-glyph', {
354
+ boxShadow: vars.effects.focusRing,
355
+ });
356
+
357
+ globalStyle('.zylem-marker-label', {
358
+ fontSize: '10px',
359
+ whiteSpace: 'nowrap',
360
+ color: vars.colors.text,
361
+ textShadow: '0 1px 1px rgba(0,0,0,0.7)',
362
+ maxWidth: '120px',
363
+ overflow: 'hidden',
364
+ textOverflow: 'ellipsis',
365
+ });