@zylem/ui 0.8.14 → 0.9.1

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 (82) hide show
  1. package/dist/chunk-YJVLOHJL.js +1 -0
  2. package/dist/index.css +1 -1
  3. package/dist/index.d.ts +7 -1
  4. package/dist/index.js +1 -1
  5. package/dist/styles.css +1 -1
  6. package/dist/styles.js +1 -1
  7. package/package.json +1 -1
  8. package/src/components/Accordion/Accordion.tsx +6 -1
  9. package/src/components/BuildLabel/BuildLabel.tsx +0 -1
  10. package/src/components/BuildLabel/mount-build-label.ts +0 -1
  11. package/src/components/Button/Button.css.ts +85 -66
  12. package/src/components/Button/Button.tsx +9 -3
  13. package/src/components/Callout/Callout.css.ts +1 -1
  14. package/src/components/Callout/Callout.tsx +1 -1
  15. package/src/components/Card/Card.css.ts +1 -0
  16. package/src/components/Card/Card.tsx +11 -2
  17. package/src/components/Checkbox/Checkbox.tsx +4 -2
  18. package/src/components/ColorPalette/ColorPalette.tsx +1 -1
  19. package/src/components/Console/Console.css.ts +1 -1
  20. package/src/components/Console/Console.tsx +3 -0
  21. package/src/components/Dialog/Dialog.css.ts +41 -17
  22. package/src/components/Dialog/Dialog.tsx +134 -53
  23. package/src/components/FrameStrip/FrameStrip.css.ts +1 -0
  24. package/src/components/FrameStrip/FrameStrip.tsx +2 -3
  25. package/src/components/ItemPicker/ItemPicker.tsx +46 -33
  26. package/src/components/MenuButton/MenuButton.css.ts +49 -46
  27. package/src/components/MenuButton/MenuButton.tsx +17 -5
  28. package/src/components/Menubar/Menubar.css.ts +1 -1
  29. package/src/components/Mixer/Knob.tsx +10 -3
  30. package/src/components/Mixer/Mixer.css.ts +21 -0
  31. package/src/components/Mixer/MixerStrip.tsx +29 -8
  32. package/src/components/NumberField/NumberField.css.ts +32 -26
  33. package/src/components/NumberField/NumberField.tsx +7 -2
  34. package/src/components/Panel/Panel.css.ts +1 -0
  35. package/src/components/Panel/Panel.tsx +9 -1
  36. package/src/components/PianoRoll/PianoRoll.tsx +1 -0
  37. package/src/components/Progress/Progress.css.ts +1 -1
  38. package/src/components/Progress/Progress.tsx +3 -0
  39. package/src/components/Property/Property.css.ts +13 -0
  40. package/src/components/Property/Property.tsx +3 -2
  41. package/src/components/SearchInput/SearchInput.tsx +1 -1
  42. package/src/components/Select/Select.css.ts +7 -1
  43. package/src/components/Select/Select.tsx +7 -2
  44. package/src/components/Sidebar/Sidebar.css.ts +1 -0
  45. package/src/components/Sidebar/Sidebar.tsx +23 -5
  46. package/src/components/Slider/Slider.css.ts +1 -1
  47. package/src/components/Slider/Slider.tsx +4 -2
  48. package/src/components/SpriteSizePicker/SpriteSizePicker.css.ts +2 -0
  49. package/src/components/Switch/Switch.tsx +3 -1
  50. package/src/components/Tabs/Tabs.css.ts +0 -5
  51. package/src/components/Tabs/Tabs.tsx +23 -12
  52. package/src/components/TextField/TextField.tsx +4 -0
  53. package/src/components/TimelineLanes/TimelineLanes.css.ts +7 -0
  54. package/src/components/TimelineLanes/TimelineLanes.tsx +52 -4
  55. package/src/components/TimelineLanes/lanes-math.ts +51 -0
  56. package/src/components/TimelineTrack/TimelineTrack.css.ts +3 -2
  57. package/src/components/TimelineTrack/TimelineTrack.tsx +16 -3
  58. package/src/components/TitleBar/TitleBar.css.ts +1 -0
  59. package/src/components/TitleBar/TitleBar.tsx +7 -1
  60. package/src/components/Toast/Toast.tsx +1 -2
  61. package/src/components/ToolButton/ToolButton.tsx +1 -1
  62. package/src/components/Toolbar/Toolbar.tsx +6 -1
  63. package/src/components/ToolbarButton/ToolbarButton.tsx +9 -2
  64. package/src/components/ToolboxButton/ToolboxButton.tsx +2 -1
  65. package/src/components/TrackHeader/TrackHeader.css.ts +21 -0
  66. package/src/components/TrackHeader/TrackHeader.tsx +37 -12
  67. package/src/components/TransportBar/TransportBar.css.ts +5 -0
  68. package/src/components/TransportBar/TransportBar.tsx +4 -1
  69. package/src/components/Waveform/Waveform.css.ts +10 -0
  70. package/src/components/Waveform/Waveform.tsx +59 -27
  71. package/src/components/WindowControls/WindowControls.css.ts +46 -63
  72. package/src/components/WindowControls/WindowControls.tsx +7 -6
  73. package/src/components/index.ts +1 -0
  74. package/src/components/shared/Heading.tsx +30 -0
  75. package/src/fonts.css +1 -1
  76. package/src/global/accordion.css.ts +9 -5
  77. package/src/global/common.css.ts +13 -0
  78. package/src/global/editor-base.css.ts +9 -0
  79. package/src/layers/LayerContext.tsx +2 -2
  80. package/src/sprinkles.css.ts +4 -1
  81. package/src/theme.css.ts +6 -0
  82. package/dist/chunk-V47CGFWX.js +0 -1
@@ -3,7 +3,7 @@ import { type JSX, Show } from 'solid-js';
3
3
  import { NumberField } from '../NumberField/NumberField';
4
4
  import { PlaybackControls } from '../PlaybackControls/PlaybackControls';
5
5
  import { Select } from '../Select/Select';
6
- import { formatBeats, formatClock, type TimeUnit } from '../TimelineLanes/lanes-math';
6
+ import { describeTime, formatBeats, formatClock, type TimeUnit } from '../TimelineLanes/lanes-math';
7
7
  import { TimelineTrack } from '../TimelineTrack/TimelineTrack';
8
8
  import { ToolbarButton } from '../ToolbarButton/ToolbarButton';
9
9
 
@@ -107,6 +107,8 @@ export function TransportBar(props: TransportBarProps) {
107
107
  max={props.length}
108
108
  onChange={(value) => props.onSeek?.(value)}
109
109
  disabled={props.disabled || !props.onSeek}
110
+ aria-label="Position"
111
+ valueText={(value) => describeTime(value, { unit: unit(), beatsPerBar: bpb() })}
110
112
  class="zylem-transport-track"
111
113
  />
112
114
  <span class="zylem-transport-time" aria-live="off">
@@ -133,6 +135,7 @@ export function TransportBar(props: TransportBarProps) {
133
135
  <Show when={props.timeSignature}>
134
136
  {(signature) => (
135
137
  <Select
138
+ aria-label="Time signature"
136
139
  options={signatureOptions()}
137
140
  value={signatureKey(signature())}
138
141
  disabled={props.disabled || !props.onTimeSignatureChange}
@@ -66,6 +66,16 @@ globalStyle('.zylem-waveform-edge::after', {
66
66
  boxSizing: 'border-box',
67
67
  });
68
68
 
69
+ // The ring goes on the grip: around the full-height edge it would be clipped.
70
+ globalStyle('.zylem-waveform-edge:focus-visible', {
71
+ outline: 'none',
72
+ boxShadow: 'none',
73
+ });
74
+
75
+ globalStyle('.zylem-waveform-edge:focus-visible::after', {
76
+ boxShadow: vars.effects.focusRing,
77
+ });
78
+
69
79
  globalStyle('.zylem-waveform-playhead', {
70
80
  position: 'absolute',
71
81
  top: 0,
@@ -1,7 +1,15 @@
1
- import { createEffect, createMemo, createSignal, onCleanup, onMount, Show } from 'solid-js';
2
- import { clamp } from '../TimelineLanes/lanes-math';
1
+ import { createEffect, createMemo, createSignal, For, onCleanup, onMount, Show } from 'solid-js';
2
+ import { clamp, describeSeconds, sliderKeyValue } from '../TimelineLanes/lanes-math';
3
3
  import { computePeaks, peaksFromAudioBuffer } from './peaks';
4
4
 
5
+ /** Seconds the trim edges must stay apart. */
6
+ const MIN_REGION = 0.01;
7
+
8
+ const EDGES = [
9
+ { edge: 'start', label: 'Trim start' },
10
+ { edge: 'end', label: 'Trim end' },
11
+ ] as const;
12
+
5
13
  export type { PeakSource } from './peaks';
6
14
 
7
15
  export interface WaveformRegion {
@@ -17,7 +25,10 @@ export interface WaveformProps {
17
25
  buffer?: { duration: number; numberOfChannels: number; getChannelData(channel: number): Float32Array };
18
26
  /** Seconds the peaks span; defaults to `buffer.duration`. Needed for regions and the playhead. */
19
27
  duration?: number;
20
- /** Highlighted span with draggable edges. */
28
+ /**
29
+ * Highlighted span with draggable edges. Focused, an edge also takes the
30
+ * arrow keys (1% of the duration, 10% with Shift) and Home / End.
31
+ */
21
32
  region?: WaveformRegion;
22
33
  onRegionChange?: (region: WaveformRegion) => void;
23
34
  onRegionCommit?: (region: WaveformRegion) => void;
@@ -106,14 +117,15 @@ export function Waveform(props: WaveformProps) {
106
117
  event.preventDefault();
107
118
  event.stopPropagation();
108
119
  const target = event.currentTarget as HTMLElement;
120
+ target.focus({ preventScroll: true });
109
121
  target.setPointerCapture?.(event.pointerId);
110
122
  let last = region;
111
123
  const move = (moveEvent: PointerEvent) => {
112
124
  const seconds = secondsAt(moveEvent.clientX);
113
125
  last =
114
126
  edge === 'start'
115
- ? { start: Math.min(seconds, region.end - 0.01), end: region.end }
116
- : { start: region.start, end: Math.max(seconds, region.start + 0.01) };
127
+ ? { start: Math.min(seconds, region.end - MIN_REGION), end: region.end }
128
+ : { start: region.start, end: Math.max(seconds, region.start + MIN_REGION) };
117
129
  props.onRegionChange?.(last);
118
130
  };
119
131
  const stop = () => {
@@ -128,6 +140,29 @@ export function Waveform(props: WaveformProps) {
128
140
  target.addEventListener('pointercancel', stop);
129
141
  };
130
142
 
143
+ /** The span each edge may move in, short of crossing the other edge. */
144
+ const edgeBounds = (edge: 'start' | 'end', region: WaveformRegion) =>
145
+ edge === 'start'
146
+ ? { min: 0, max: Math.max(0, region.end - MIN_REGION) }
147
+ : { min: Math.min(duration(), region.start + MIN_REGION), max: duration() };
148
+
149
+ const nudgeEdge = (edge: 'start' | 'end') => (event: KeyboardEvent) => {
150
+ const region = props.region;
151
+ if (props.disabled || !region) return;
152
+ const next = sliderKeyValue(event, {
153
+ value: region[edge],
154
+ ...edgeBounds(edge, region),
155
+ step: duration() / 100,
156
+ });
157
+ if (next === null) return;
158
+ event.preventDefault();
159
+ // Page-level shortcuts (Home to rewind, arrows to seek) must not also act on it.
160
+ event.stopPropagation();
161
+ const changed = { ...region, [edge]: next };
162
+ props.onRegionChange?.(changed);
163
+ props.onRegionCommit?.(changed);
164
+ };
165
+
131
166
  return (
132
167
  <div
133
168
  ref={host}
@@ -153,28 +188,25 @@ export function Waveform(props: WaveformProps) {
153
188
  class="zylem-waveform-shade"
154
189
  style={{ left: `${percent(region().end)}%`, right: 0 }}
155
190
  />
156
- <div
157
- class="zylem-waveform-edge"
158
- data-edge="start"
159
- style={{ left: `${percent(region().start)}%` }}
160
- onPointerDown={beginRegionDrag('start')}
161
- role="slider"
162
- aria-label="Trim start"
163
- aria-valuenow={region().start}
164
- aria-valuemin={0}
165
- aria-valuemax={duration()}
166
- />
167
- <div
168
- class="zylem-waveform-edge"
169
- data-edge="end"
170
- style={{ left: `${percent(region().end)}%` }}
171
- onPointerDown={beginRegionDrag('end')}
172
- role="slider"
173
- aria-label="Trim end"
174
- aria-valuenow={region().end}
175
- aria-valuemin={0}
176
- aria-valuemax={duration()}
177
- />
191
+ <For each={EDGES}>
192
+ {(edge) => (
193
+ <div
194
+ class="zylem-waveform-edge"
195
+ data-edge={edge.edge}
196
+ style={{ left: `${percent(region()[edge.edge])}%` }}
197
+ onPointerDown={beginRegionDrag(edge.edge)}
198
+ onKeyDown={nudgeEdge(edge.edge)}
199
+ role="slider"
200
+ tabIndex={props.disabled ? -1 : 0}
201
+ aria-label={edge.label}
202
+ aria-valuenow={region()[edge.edge]}
203
+ aria-valuemin={edgeBounds(edge.edge, region()).min}
204
+ aria-valuemax={edgeBounds(edge.edge, region()).max}
205
+ aria-valuetext={describeSeconds(region()[edge.edge])}
206
+ aria-disabled={props.disabled ? 'true' : undefined}
207
+ />
208
+ )}
209
+ </For>
178
210
  </>
179
211
  );
180
212
  }}
@@ -1,89 +1,72 @@
1
1
  import { globalStyle } from '@vanilla-extract/css';
2
2
  import { vars } from '../../theme.css';
3
3
 
4
- /** Soft drop plus a light top rim and a darker lower edge. */
5
- const jewelDepth =
6
- 'inset 0 1px 0 rgba(255,255,255,0.72), inset 0 -1px 0 rgba(0,0,0,0.28), 0 2px 4px rgba(0,0,0,0.4)';
7
-
8
4
  globalStyle('.zylem-window-controls', {
9
- display: 'flex',
10
- alignItems: 'center',
11
- flexShrink: 0,
12
- gap: '6px',
5
+ display: 'flex',
6
+ alignItems: 'center',
7
+ flexShrink: 0,
8
+ gap: '6px',
13
9
  });
14
10
 
15
11
  /**
16
- * Shared glass jewel used for all panel chrome: window controls, floating
12
+ * Shared ghost control used for panel chrome: window controls, floating
17
13
  * panels, and detached panels. `.floating-panel-button` is kept as an alias
18
14
  * so existing panel markup shares this single definition.
19
15
  *
20
- * Collapse (and the untyped alias) is blue glass; close is red glass.
16
+ * Resting controls match the ghost button. Close stays that way until it is
17
+ * hovered or focused, when it takes the danger color.
21
18
  */
22
19
  globalStyle('.zylem-window-control, .floating-panel-button', {
23
- all: 'unset',
24
- boxSizing: 'border-box',
25
- position: 'relative',
26
- cursor: 'pointer',
27
- flexShrink: 0,
28
- width: '28px',
29
- height: '18px',
30
- display: 'inline-flex',
31
- alignItems: 'center',
32
- justifyContent: 'center',
33
- color: '#FFFFFF',
34
- borderRadius: '6px',
35
- border: '1px solid rgba(214, 234, 255, 0.82)',
36
- background:
37
- 'linear-gradient(180deg, rgba(206, 230, 255, 0.78) 0%, rgba(96, 158, 216, 0.84) 42%, rgba(48, 108, 172, 0.92) 100%)',
38
- boxShadow: `${jewelDepth}, 0 0 8px rgba(97, 166, 232, 0.28)`,
39
- transition: `filter ${vars.motion.fast} ${vars.motion.easeOut}, transform ${vars.motion.fast} ${vars.motion.easeOut}, background ${vars.motion.fast} ${vars.motion.easeOut}, border-color ${vars.motion.fast} ${vars.motion.easeOut}, box-shadow ${vars.motion.fast} ${vars.motion.easeOut}`,
40
- });
41
-
42
- /** Top-edge gloss. Clipped by the button; the icon sits above it. */
43
- globalStyle('.zylem-window-control::before, .floating-panel-button::before', {
44
- content: '""',
45
- position: 'absolute',
46
- left: '2px',
47
- right: '2px',
48
- top: '1px',
49
- height: '48%',
50
- borderRadius: '5px 5px 46% 46%',
51
- background:
52
- 'linear-gradient(180deg, rgba(255,255,255,0.55), rgba(255,255,255,0.1) 55%, rgba(255,255,255,0))',
53
- pointerEvents: 'none',
20
+ all: 'unset',
21
+ boxSizing: 'border-box',
22
+ position: 'relative',
23
+ cursor: 'pointer',
24
+ flexShrink: 0,
25
+ width: '28px',
26
+ height: '18px',
27
+ display: 'inline-flex',
28
+ alignItems: 'center',
29
+ justifyContent: 'center',
30
+ color: vars.colors.primary,
31
+ borderRadius: '6px',
32
+ border: '1px solid rgba(97, 166, 232, 0.35)',
33
+ background: 'transparent',
34
+ boxShadow: 'none',
35
+ transition: `color ${vars.motion.fast} ${vars.motion.easeOut}, transform ${vars.motion.fast} ${vars.motion.easeOut}, background ${vars.motion.fast} ${vars.motion.easeOut}, border-color ${vars.motion.fast} ${vars.motion.easeOut}, box-shadow ${vars.motion.fast} ${vars.motion.easeOut}`,
54
36
  });
55
37
 
56
38
  globalStyle('.zylem-window-control svg, .floating-panel-button svg', {
57
- position: 'relative',
58
- zIndex: 1,
59
- display: 'block',
60
- filter: 'drop-shadow(0 1px 1px rgba(0,0,0,0.4))',
61
- });
62
-
63
- globalStyle('.zylem-window-control--close', {
64
- borderColor: 'rgba(255, 198, 188, 0.88)',
65
- background:
66
- 'linear-gradient(180deg, rgba(255, 196, 186, 0.78) 0%, rgba(228, 82, 70, 0.9) 44%, rgba(186, 46, 40, 0.96) 100%)',
67
- boxShadow: `${jewelDepth}, 0 0 8px rgba(230, 69, 52, 0.34)`,
39
+ position: 'relative',
40
+ zIndex: 1,
41
+ display: 'block',
68
42
  });
69
43
 
70
44
  globalStyle('.zylem-window-control:hover, .floating-panel-button:hover', {
71
- filter: 'brightness(1.12)',
72
- transform: 'translateY(-1px)',
45
+ background: 'rgba(97, 166, 232, 0.12)',
46
+ boxShadow: vars.effects.glowPrimary,
47
+ color: vars.colors.primary,
48
+ transform: 'translateY(-1px)',
73
49
  });
74
50
 
75
51
  globalStyle('.zylem-window-control:active, .floating-panel-button:active', {
76
- filter: 'brightness(0.92)',
77
- transform: 'translateY(1px)',
52
+ transform: 'translateY(1px)',
53
+ });
54
+
55
+ globalStyle('.zylem-window-control:focus-visible, .floating-panel-button:focus-visible', {
56
+ background: 'rgba(97, 166, 232, 0.12)',
57
+ boxShadow: `${vars.effects.glowPrimary}, ${vars.effects.focusRing}`,
78
58
  });
79
59
 
80
- globalStyle(
81
- '.zylem-window-control:focus-visible, .floating-panel-button:focus-visible',
82
- {
83
- boxShadow: `${jewelDepth}, 0 0 8px rgba(97, 166, 232, 0.28), ${vars.effects.focusRing}`,
84
- },
85
- );
60
+ globalStyle('.zylem-window-control--close:hover', {
61
+ borderColor: 'rgba(255, 132, 114, 0.78)',
62
+ background: 'rgba(230, 69, 52, 0.16)',
63
+ color: '#ffb4a8',
64
+ boxShadow: vars.effects.glowDanger,
65
+ });
86
66
 
87
67
  globalStyle('.zylem-window-control--close:focus-visible', {
88
- boxShadow: `${jewelDepth}, 0 0 8px rgba(230, 69, 52, 0.34), ${vars.effects.focusRing}`,
68
+ borderColor: 'rgba(255, 132, 114, 0.78)',
69
+ background: 'rgba(230, 69, 52, 0.16)',
70
+ color: '#ffb4a8',
71
+ boxShadow: `${vars.effects.glowDanger}, ${vars.effects.focusRing}`,
89
72
  });
@@ -17,13 +17,14 @@ export interface WindowControlsProps {
17
17
  }
18
18
 
19
19
  /**
20
- * Shared glass window controls: blue collapse jewel and red close jewel, used
21
- * by floating and detached panels. Only controls with a handler are rendered.
20
+ * Shared ghost window controls, used by floating and detached panels. Close
21
+ * takes the danger color only while hovered or focused. Only controls with a
22
+ * handler are rendered.
22
23
  */
23
24
  export function WindowControls(props: WindowControlsProps) {
24
25
  return (
25
26
  <Show when={props.onClose || props.onCollapse}>
26
- <div class="zylem-window-controls" aria-label="Window controls">
27
+ <div class="zylem-window-controls" role="group" aria-label="Window controls">
27
28
  <Show when={props.onCollapse}>
28
29
  <button
29
30
  type="button"
@@ -33,9 +34,9 @@ export function WindowControls(props: WindowControlsProps) {
33
34
  onClick={() => props.onCollapse?.()}
34
35
  >
35
36
  {props.collapsed ? (
36
- <ChevronUp size={11} strokeWidth={2.5} />
37
+ <ChevronUp size={11} strokeWidth={2.5} aria-hidden="true" />
37
38
  ) : (
38
- <ChevronDown size={11} strokeWidth={2.5} />
39
+ <ChevronDown size={11} strokeWidth={2.5} aria-hidden="true" />
39
40
  )}
40
41
  </button>
41
42
  </Show>
@@ -48,7 +49,7 @@ export function WindowControls(props: WindowControlsProps) {
48
49
  data-testid={props.closeTestId}
49
50
  onClick={() => props.onClose?.()}
50
51
  >
51
- <X size={11} strokeWidth={2.5} />
52
+ <X size={11} strokeWidth={2.5} aria-hidden="true" />
52
53
  </button>
53
54
  </Show>
54
55
  </div>
@@ -24,6 +24,7 @@ export {
24
24
  } from '../layers';
25
25
 
26
26
  // Primitives
27
+ export { type HeadingLevel } from './shared/Heading';
27
28
  export { Button, type ButtonProps, type ButtonVariant, type ButtonSize } from './Button/Button';
28
29
  export { ToolbarButton, type ToolbarButtonProps } from './ToolbarButton/ToolbarButton';
29
30
  export { TextField, type TextFieldProps } from './TextField/TextField';
@@ -0,0 +1,30 @@
1
+ import type { JSX } from 'solid-js';
2
+ import { Dynamic } from 'solid-js/web';
3
+
4
+ /** Outline level of a component's title: `2` renders an `<h2>`. */
5
+ export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
6
+
7
+ /** The element name for a level, for primitives that take an `as` tag. */
8
+ export function headingTag(level: HeadingLevel) {
9
+ return `h${level}` as const;
10
+ }
11
+
12
+ export interface HeadingProps {
13
+ level: HeadingLevel;
14
+ id?: string | undefined;
15
+ class?: string | undefined;
16
+ title?: string | undefined;
17
+ children: JSX.Element;
18
+ }
19
+
20
+ /**
21
+ * An `<h1>`…`<h6>` chosen at runtime, for titles whose place in the page
22
+ * outline is the host's call.
23
+ */
24
+ export function Heading(props: HeadingProps) {
25
+ return (
26
+ <Dynamic component={headingTag(props.level)} id={props.id} class={props.class} title={props.title}>
27
+ {props.children}
28
+ </Dynamic>
29
+ );
30
+ }
package/src/fonts.css CHANGED
@@ -1 +1 @@
1
- @import url('https://fonts.googleapis.com/css2?family=Exo+2:ital,wght@0,100..900;1,100..900&display=swap');
1
+ @import url('https://fonts.googleapis.com/css2?family=Exo+2:ital,wght@0,100..900;1,100..900&family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&display=swap');
@@ -14,11 +14,14 @@ globalStyle('.zylem-accordion', {
14
14
  minHeight: 0,
15
15
  });
16
16
 
17
+ // The header is a heading element; its UA margins would open gaps around the
18
+ // trigger, and differ by heading level.
17
19
  globalStyle('.accordion-header', {
18
20
  position: 'sticky',
19
21
  top: 0,
20
22
  zIndex: 1,
21
23
  display: 'flex',
24
+ margin: 0,
22
25
  });
23
26
 
24
27
  globalStyle('.accordion-item', {
@@ -104,15 +107,16 @@ globalStyle('.zylem-accordion--stack .accordion-content', {
104
107
  overflow: 'visible',
105
108
  });
106
109
 
107
- globalStyle('.accordion-trigger::after', {
108
- content: "'\\25BC'",
109
- position: 'relative',
110
+ globalStyle('.accordion-chevron', {
111
+ width: '16px',
112
+ height: '16px',
113
+ flexShrink: 0,
114
+ stroke: 'currentColor',
110
115
  color: vars.colors.textSecondary,
111
- fontSize: '10px',
112
116
  transition: `transform ${vars.motion.normal} ${vars.motion.easeOut}`,
113
117
  });
114
118
 
115
- globalStyle('.accordion-trigger[data-expanded]::after', {
119
+ globalStyle('.accordion-trigger[data-expanded] .accordion-chevron', {
116
120
  transform: 'rotate(180deg)',
117
121
  color: '#F4FAFF',
118
122
  });
@@ -13,6 +13,19 @@ globalStyle('.overlay', {
13
13
  pointerEvents: 'none',
14
14
  });
15
15
 
16
+ // Text for assistive tech only: off screen, still in the accessibility tree.
17
+ globalStyle('.zylem-visually-hidden', {
18
+ position: 'absolute',
19
+ width: '1px',
20
+ height: '1px',
21
+ padding: 0,
22
+ margin: '-1px',
23
+ overflow: 'hidden',
24
+ clip: 'rect(0, 0, 0, 0)',
25
+ whiteSpace: 'nowrap',
26
+ border: 0,
27
+ });
28
+
16
29
  // Default scrollbars: slim glass rail (use `.zylem-scroll` for the full
17
30
  // 13px HyperGlass treatment defined in hyperglass-base.css.ts).
18
31
  globalStyle('::-webkit-scrollbar', {
@@ -37,6 +37,15 @@ globalStyle('.zylem-exo-2', {
37
37
  fontStyle: 'normal',
38
38
  });
39
39
 
40
+ /** Time and units: clocks, beats, dimensions, levels. */
41
+ globalStyle('.zylem-mono', {
42
+ fontFamily: vars.typography.fontFamilyMono,
43
+ fontOpticalSizing: 'auto',
44
+ fontWeight: 400,
45
+ fontStyle: 'normal',
46
+ fontVariantNumeric: 'tabular-nums',
47
+ });
48
+
40
49
  globalStyle('.scrollable-y', {
41
50
  overflowY: 'scroll',
42
51
  overflowX: 'hidden',
@@ -60,8 +60,8 @@ export interface LayerScopeProps {
60
60
  * them can paint above anything outside; and the scope owns the portal container
61
61
  * its descendants mount into, so overlays opened inside it stay inside it.
62
62
  *
63
- * A dialog wraps its content in one, so a tooltip opened within the dialog floats
64
- * over the dialog body without also floating over a menu belonging to the page.
63
+ * A dialog puts its card in one, so a menu opened within the dialog mounts
64
+ * inside the `<dialog>`, floats over the card, and is not clipped by it.
65
65
  */
66
66
  export function LayerScope(props: LayerScopeProps) {
67
67
  const [container, setContainer] = createSignal<HTMLElement>();
@@ -149,7 +149,10 @@ const visual = defineProperties({
149
149
  bold: '2px',
150
150
  },
151
151
  borderStyle: ['solid', 'dashed', 'dotted', 'none'],
152
- fontFamily: { default: vars.typography.fontFamily },
152
+ fontFamily: {
153
+ default: vars.typography.fontFamily,
154
+ mono: vars.typography.fontFamilyMono,
155
+ },
153
156
  fontSize: { default: vars.typography.fontSize },
154
157
  lineHeight: ['1', '1.2', '1.35', '1.45', '1.5'],
155
158
  letterSpacing: {
package/src/theme.css.ts CHANGED
@@ -53,8 +53,13 @@ const tokens = {
53
53
  iconSm: '16px',
54
54
  iconLg: '96px',
55
55
  },
56
+ /**
57
+ * Exo 2 is the interface face. JetBrains Mono is for time and units
58
+ * (clocks, beats, dimensions, levels) so digits keep a stable width.
59
+ */
56
60
  typography: {
57
61
  fontFamily: "'Exo 2', sans-serif",
62
+ fontFamilyMono: "'JetBrains Mono', ui-monospace, monospace",
58
63
  fontSize: '14px',
59
64
  },
60
65
  borders: {
@@ -178,6 +183,7 @@ const varNames = {
178
183
  },
179
184
  typography: {
180
185
  fontFamily: 'zylem-font-family',
186
+ fontFamilyMono: 'zylem-font-family-mono',
181
187
  fontSize: 'zylem-font-size',
182
188
  },
183
189
  borders: {
@@ -1 +0,0 @@
1
- var l={colors:{primary:"#61A6E8",primaryHover:"#3B8AD8",primaryActive:"#0C6EB8",accent:"#E64534",accentHover:"#C93A2C",accentActive:"#A82F24",background:"#11151C",backgroundTranslucent:"rgba(10, 20, 30, 0.46)",surface:"#1A1F27",surfaceHover:"#222831",border:"#2D3643",active:"rgba(20, 255, 60, 0.7)",activeHover:"rgba(20, 255, 60, 0.4)",successHover:"#2a6734b3",text:"#E6EBEF",textSecondary:"#88929E",consoleBackground:"rgba(20, 40, 60, 0.25)",consoleText:"#61A6E8"},spacing:{xxs:"0.125rem",xs:"0.25rem",sm:"0.5rem",md:"0.75rem",lg:"1rem",xl:"1.5rem",xxl:"2rem",xxxl:"3rem"},sizes:{icon:"28px",iconSm:"16px",iconLg:"96px"},typography:{fontFamily:"'Exo 2', sans-serif",fontSize:"14px"},borders:{radius:"12px",width:"1.5px"},material:{glassPanel:"linear-gradient(180deg, rgba(38, 76, 112, 0.74), rgba(10, 24, 38, 0.58))",glassPanelDark:"linear-gradient(180deg, rgba(26, 31, 39, 0.75), rgba(10, 20, 30, 0.68))",glossyBar:"linear-gradient(180deg, rgba(205, 232, 255, 0.55) 0%, rgba(97, 166, 232, 0.34) 38%, rgba(18, 54, 88, 0.86) 100%)",buttonGlass:"linear-gradient(180deg, rgba(178, 219, 255, 0.34), rgba(30, 94, 148, 0.72) 48%, rgba(9, 34, 58, 0.92))",buttonGlassHover:"linear-gradient(180deg, rgba(218, 242, 255, 0.58), rgba(48, 137, 220, 0.86) 48%, rgba(11, 62, 112, 0.96))",buttonGlassActive:"linear-gradient(180deg, rgba(190, 255, 203, 0.42), rgba(28, 174, 63, 0.78) 50%, rgba(9, 86, 31, 0.95))",buttonGlassDanger:"linear-gradient(180deg, rgba(255, 210, 202, 0.36), rgba(230, 69, 52, 0.82) 50%, rgba(96, 18, 13, 0.94))",fieldGlass:"linear-gradient(180deg, rgba(8, 19, 31, 0.82), rgba(15, 39, 62, 0.72))",panelHeaderGloss:"linear-gradient(180deg, rgba(190, 226, 255, 0.18), rgba(61, 118, 165, 0.16) 46%, rgba(10, 27, 44, 0.26))"},effects:{blurSm:"8px",blurMd:"14px",blurLg:"24px",shadowPanel:"0 22px 54px rgba(0, 0, 0, 0.48), inset 0 1px 0 rgba(255, 255, 255, 0.10)",shadowButton:"inset 0 1px 0 rgba(255,255,255,0.38), inset 0 -1px 0 rgba(0,0,0,0.45), 0 2px 7px rgba(0,0,0,0.35)",shadowInset:"inset 0 1px 2px rgba(0,0,0,0.42), inset 0 -1px 0 rgba(255,255,255,0.08)",glowPrimary:"0 0 18px rgba(97, 166, 232, 0.28)",glowActive:"0 0 20px rgba(20, 255, 60, 0.26)",glowDanger:"0 0 20px rgba(230, 69, 52, 0.24)",focusRing:"0 0 0 2px rgba(97, 166, 232, 0.38), 0 0 18px rgba(97, 166, 232, 0.22)"},radii:{window:"14px",panel:"12px",button:"7px",control:"6px",card:"10px"},motion:{fast:"120ms",normal:"160ms",easeOut:"cubic-bezier(.2,.8,.2,1)"},art:{checkerLight:"#4F5C72",checkerDark:"#3C4759",canvasBorder:"rgba(150, 210, 255, 0.5)"},layers:{base:"0",panel:"1000",modal:"2000",popover:"3000",tooltip:"4000",menu:"5000"}},o={colors:{primary:"var(--zylem-color-primary)",primaryHover:"var(--zylem-color-primary-hover)",primaryActive:"var(--zylem-color-primary-active)",accent:"var(--zylem-color-accent)",accentHover:"var(--zylem-color-accent-hover)",accentActive:"var(--zylem-color-accent-active)",background:"var(--zylem-color-background)",backgroundTranslucent:"var(--zylem-color-background-translucent)",surface:"var(--zylem-color-surface)",surfaceHover:"var(--zylem-color-surface-hover)",border:"var(--zylem-color-border)",active:"var(--zylem-color-active)",activeHover:"var(--zylem-color-active-hover)",successHover:"var(--zylem-color-success-hover)",text:"var(--zylem-color-text)",textSecondary:"var(--zylem-color-text-secondary)",consoleBackground:"var(--zylem-color-console-background)",consoleText:"var(--zylem-color-console-text)"},spacing:{xxs:"var(--zylem-spacing-xxs)",xs:"var(--zylem-spacing-xs)",sm:"var(--zylem-spacing-sm)",md:"var(--zylem-spacing-md)",lg:"var(--zylem-spacing-lg)",xl:"var(--zylem-spacing-xl)",xxl:"var(--zylem-spacing-xxl)",xxxl:"var(--zylem-spacing-xxxl)"},sizes:{icon:"var(--zylem-size-icon)",iconSm:"var(--zylem-size-icon-sm)",iconLg:"var(--zylem-size-icon-lg)"},typography:{fontFamily:"var(--zylem-font-family)",fontSize:"var(--zylem-font-size)"},borders:{radius:"var(--zylem-radius)",width:"var(--zylem-border)"},material:{glassPanel:"var(--zylem-material-glass-panel)",glassPanelDark:"var(--zylem-material-glass-panel-dark)",glossyBar:"var(--zylem-material-glossy-bar)",buttonGlass:"var(--zylem-material-button-glass)",buttonGlassHover:"var(--zylem-material-button-glass-hover)",buttonGlassActive:"var(--zylem-material-button-glass-active)",buttonGlassDanger:"var(--zylem-material-button-glass-danger)",fieldGlass:"var(--zylem-material-field-glass)",panelHeaderGloss:"var(--zylem-material-panel-header-gloss)"},effects:{blurSm:"var(--zylem-effect-blur-sm)",blurMd:"var(--zylem-effect-blur-md)",blurLg:"var(--zylem-effect-blur-lg)",shadowPanel:"var(--zylem-effect-shadow-panel)",shadowButton:"var(--zylem-effect-shadow-button)",shadowInset:"var(--zylem-effect-shadow-inset)",glowPrimary:"var(--zylem-effect-glow-primary)",glowActive:"var(--zylem-effect-glow-active)",glowDanger:"var(--zylem-effect-glow-danger)",focusRing:"var(--zylem-effect-focus-ring)"},radii:{window:"var(--zylem-radius-window)",panel:"var(--zylem-radius-panel)",button:"var(--zylem-radius-button)",control:"var(--zylem-radius-control)",card:"var(--zylem-radius-card)"},motion:{fast:"var(--zylem-motion-fast)",normal:"var(--zylem-motion-normal)",easeOut:"var(--zylem-motion-ease-out)"},art:{checkerLight:"var(--zylem-art-checker-light)",checkerDark:"var(--zylem-art-checker-dark)",canvasBorder:"var(--zylem-art-canvas-border)"},layers:{base:"var(--zylem-layer-base)",panel:"var(--zylem-layer-panel)",modal:"var(--zylem-layer-modal)",popover:"var(--zylem-layer-popover)",tooltip:"var(--zylem-layer-tooltip)",menu:"var(--zylem-layer-menu)"}};export{l as a,o as b};