@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
package/dist/styles.js CHANGED
@@ -1 +1 @@
1
- import"./chunk-V47CGFWX.js";
1
+ import"./chunk-YJVLOHJL.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zylem/ui",
3
- "version": "0.8.14",
3
+ "version": "0.9.1",
4
4
  "description": "Design tokens, global styles, and Solid components for Zylem, built with vanilla-extract",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,6 +1,8 @@
1
1
  import { Accordion as KAccordion } from '@kobalte/core';
2
+ import ChevronDown from 'lucide-solid/icons/chevron-down';
2
3
  import { For } from 'solid-js';
3
4
  import type { JSX } from 'solid-js';
5
+ import { headingTag, type HeadingLevel } from '../shared/Heading';
4
6
 
5
7
  export interface AccordionItemConfig {
6
8
  id: string;
@@ -16,6 +18,8 @@ export interface AccordionProps {
16
18
  onChange?: (value: string[]) => void;
17
19
  defaultValue?: string[];
18
20
  class?: string;
21
+ /** Heading level of each section header (default 3). */
22
+ headingLevel?: HeadingLevel;
19
23
  /** Extra class applied to each content region (e.g. scroll helpers). */
20
24
  contentClass?: string;
21
25
  /**
@@ -47,9 +51,10 @@ export function Accordion(props: AccordionProps) {
47
51
  <For each={props.items}>
48
52
  {(item) => (
49
53
  <KAccordion.Item value={item.id} class="accordion-item">
50
- <KAccordion.Header class="accordion-header">
54
+ <KAccordion.Header as={headingTag(props.headingLevel ?? 3)} class="accordion-header">
51
55
  <KAccordion.Trigger class="accordion-trigger">
52
56
  {item.title}
57
+ <ChevronDown class="accordion-chevron" aria-hidden="true" />
53
58
  </KAccordion.Trigger>
54
59
  </KAccordion.Header>
55
60
  <KAccordion.Content
@@ -28,7 +28,6 @@ export function BuildLabel(props: BuildLabelProps) {
28
28
  <div
29
29
  id={BUILD_LABEL_ID}
30
30
  class="zylem-build-label"
31
- aria-label="Build version"
32
31
  title={title()}
33
32
  >
34
33
  {label()}
@@ -62,7 +62,6 @@ export function mountBuildLabel(
62
62
  const el = document.createElement('div');
63
63
  el.id = BUILD_LABEL_ID;
64
64
  el.className = 'zylem-build-label';
65
- el.setAttribute('aria-label', 'Build version');
66
65
  el.textContent = formatBuildLabel(resolveBuildVersion(model));
67
66
  const title = packagesTitle(model.packages);
68
67
  if (title) el.title = title;
@@ -9,119 +9,138 @@ import { vars } from '../../theme.css';
9
9
  */
10
10
 
11
11
  globalStyle('.zylem-button', {
12
- boxSizing: 'border-box',
13
- display: 'inline-flex',
14
- alignItems: 'center',
15
- justifyContent: 'center',
16
- gap: vars.spacing.xs,
17
- minHeight: '28px',
18
- padding: `0 ${vars.spacing.md}`,
19
- cursor: 'pointer',
20
- userSelect: 'none',
21
- whiteSpace: 'nowrap',
22
- borderRadius: vars.radii.button,
23
- border: '1px solid rgba(150, 210, 255, 0.55)',
24
- color: '#F4FAFF',
25
- background: vars.material.buttonGlass,
26
- boxShadow: vars.effects.shadowButton,
27
- textShadow: '0 1px 1px rgba(0,0,0,0.72)',
28
- fontFamily: vars.typography.fontFamily,
29
- fontSize: vars.typography.fontSize,
30
- fontWeight: 600,
31
- transition: `transform ${vars.motion.fast} ${vars.motion.easeOut}, box-shadow ${vars.motion.fast} ${vars.motion.easeOut}, filter ${vars.motion.fast} ${vars.motion.easeOut}, background ${vars.motion.fast} ${vars.motion.easeOut}`,
12
+ boxSizing: 'border-box',
13
+ display: 'inline-flex',
14
+ alignItems: 'center',
15
+ justifyContent: 'center',
16
+ gap: vars.spacing.xs,
17
+ minHeight: '28px',
18
+ padding: `0 ${vars.spacing.md}`,
19
+ cursor: 'pointer',
20
+ userSelect: 'none',
21
+ whiteSpace: 'nowrap',
22
+ borderRadius: vars.radii.button,
23
+ border: '1px solid rgba(150, 210, 255, 0.55)',
24
+ color: '#F4FAFF',
25
+ background: vars.material.buttonGlass,
26
+ boxShadow: vars.effects.shadowButton,
27
+ textShadow: '0 1px 1px rgba(0,0,0,0.72)',
28
+ fontFamily: vars.typography.fontFamily,
29
+ fontSize: vars.typography.fontSize,
30
+ fontWeight: 600,
31
+ transition: `transform ${vars.motion.fast} ${vars.motion.easeOut}, box-shadow ${vars.motion.fast} ${vars.motion.easeOut}, filter ${vars.motion.fast} ${vars.motion.easeOut}, background ${vars.motion.fast} ${vars.motion.easeOut}`,
32
32
  });
33
33
 
34
34
  globalStyle('.zylem-button:hover:not([disabled])', {
35
- background: vars.material.buttonGlassHover,
36
- boxShadow: `${vars.effects.shadowButton}, ${vars.effects.glowPrimary}`,
37
- transform: 'translateY(-1px)',
38
- color: '#F4FAFF',
35
+ background: vars.material.buttonGlassHover,
36
+ boxShadow: `${vars.effects.shadowButton}, ${vars.effects.glowPrimary}`,
37
+ transform: 'translateY(-1px)',
38
+ color: '#F4FAFF',
39
39
  });
40
40
 
41
41
  globalStyle('.zylem-button:active:not([disabled])', {
42
- transform: 'translateY(1px)',
43
- filter: 'brightness(0.92)',
44
- // CodePen-inspired press: compressed drop shadow + bright bottom rim flash.
45
- boxShadow:
46
- 'inset 0 1px 0 rgba(255,255,255,0.22), inset 0 -1px 0 rgba(255,255,255,0.55), inset 0 2px 3px rgba(0,0,0,0.28), 0 1px 3px rgba(0,0,0,0.28)',
42
+ transform: 'translateY(1px)',
43
+ filter: 'brightness(0.92)',
44
+ // CodePen-inspired press: compressed drop shadow + bright bottom rim flash.
45
+ boxShadow:
46
+ 'inset 0 1px 0 rgba(255,255,255,0.22), inset 0 -1px 0 rgba(255,255,255,0.55), inset 0 2px 3px rgba(0,0,0,0.28), 0 1px 3px rgba(0,0,0,0.28)',
47
47
  });
48
48
 
49
49
  globalStyle('.zylem-button:focus-visible', {
50
- outline: 'none',
51
- boxShadow: `${vars.effects.shadowButton}, ${vars.effects.focusRing}`,
50
+ outline: 'none',
51
+ boxShadow: `${vars.effects.shadowButton}, ${vars.effects.focusRing}`,
52
52
  });
53
53
 
54
54
  globalStyle('.zylem-button[disabled], .zylem-button[data-disabled]', {
55
- opacity: 0.45,
56
- cursor: 'not-allowed',
55
+ opacity: 0.45,
56
+ cursor: 'not-allowed',
57
57
  });
58
58
 
59
59
  // Variants
60
60
 
61
61
  globalStyle('.zylem-button[data-variant="primary"]', {
62
- borderColor: 'rgba(178, 219, 255, 0.8)',
63
- background: vars.material.buttonGlassHover,
62
+ borderColor: 'rgba(178, 219, 255, 0.8)',
63
+ background: vars.material.buttonGlassHover,
64
64
  });
65
65
 
66
66
  globalStyle('.zylem-button[data-variant="primary"]:hover:not([disabled])', {
67
- filter: 'brightness(1.08)',
67
+ filter: 'brightness(1.08)',
68
68
  });
69
69
 
70
70
  globalStyle('.zylem-button[data-variant="active"]', {
71
- borderColor: 'rgba(20, 255, 60, 0.78)',
72
- background: vars.material.buttonGlassActive,
73
- boxShadow: `${vars.effects.shadowButton}, ${vars.effects.glowActive}`,
71
+ borderColor: 'rgba(20, 255, 60, 0.78)',
72
+ background: vars.material.buttonGlassActive,
73
+ boxShadow: `${vars.effects.shadowButton}, ${vars.effects.glowActive}`,
74
74
  });
75
75
 
76
76
  globalStyle('.zylem-button[data-variant="active"]:hover:not([disabled])', {
77
- background: vars.material.buttonGlassActive,
78
- boxShadow: `${vars.effects.shadowButton}, ${vars.effects.glowActive}`,
79
- filter: 'brightness(1.1)',
77
+ background: vars.material.buttonGlassActive,
78
+ boxShadow: `${vars.effects.shadowButton}, ${vars.effects.glowActive}`,
79
+ filter: 'brightness(1.1)',
80
80
  });
81
81
 
82
82
  globalStyle('.zylem-button[data-variant="danger"]', {
83
- borderColor: 'rgba(255, 132, 114, 0.78)',
84
- background: vars.material.buttonGlassDanger,
85
- boxShadow: `${vars.effects.shadowButton}, ${vars.effects.glowDanger}`,
83
+ borderColor: 'rgba(255, 132, 114, 0.78)',
84
+ background: vars.material.buttonGlassDanger,
85
+ boxShadow: `${vars.effects.shadowButton}, ${vars.effects.glowDanger}`,
86
86
  });
87
87
 
88
88
  globalStyle('.zylem-button[data-variant="danger"]:hover:not([disabled])', {
89
- background: vars.material.buttonGlassDanger,
90
- boxShadow: `${vars.effects.shadowButton}, ${vars.effects.glowDanger}`,
91
- filter: 'brightness(1.1)',
89
+ background: vars.material.buttonGlassDanger,
90
+ boxShadow: `${vars.effects.shadowButton}, ${vars.effects.glowDanger}`,
91
+ filter: 'brightness(1.1)',
92
92
  });
93
93
 
94
94
  globalStyle('.zylem-button[data-variant="ghost"]', {
95
- border: '1px solid rgba(97, 166, 232, 0.35)',
96
- background: 'transparent',
97
- boxShadow: 'none',
98
- color: vars.colors.primary,
99
- textShadow: 'none',
95
+ border: '1px solid rgba(97, 166, 232, 0.35)',
96
+ background: 'transparent',
97
+ boxShadow: 'none',
98
+ color: vars.colors.primary,
99
+ textShadow: 'none',
100
100
  });
101
101
 
102
102
  globalStyle('.zylem-button[data-variant="ghost"]:hover:not([disabled])', {
103
- background: 'rgba(97, 166, 232, 0.12)',
104
- boxShadow: vars.effects.glowPrimary,
105
- color: vars.colors.primary,
103
+ background: 'rgba(97, 166, 232, 0.12)',
104
+ boxShadow: vars.effects.glowPrimary,
105
+ color: vars.colors.primary,
106
106
  });
107
107
 
108
108
  // Sizes
109
109
 
110
110
  globalStyle('.zylem-button[data-size="sm"]', {
111
- minHeight: '22px',
112
- padding: `0 ${vars.spacing.sm}`,
113
- fontSize: `calc(${vars.typography.fontSize} - 2px)`,
111
+ minHeight: '22px',
112
+ padding: `0 ${vars.spacing.sm}`,
113
+ fontSize: `calc(${vars.typography.fontSize} - 2px)`,
114
114
  });
115
115
 
116
116
  globalStyle('.zylem-button[data-size="lg"]', {
117
- minHeight: '36px',
118
- padding: `0 ${vars.spacing.lg}`,
119
- fontSize: `calc(${vars.typography.fontSize} + 1px)`,
117
+ minHeight: '36px',
118
+ padding: `0 ${vars.spacing.lg}`,
119
+ fontSize: `calc(${vars.typography.fontSize} + 4px)`,
120
120
  });
121
121
 
122
122
  globalStyle('.zylem-button[data-size="icon"]', {
123
- minHeight: '28px',
124
- minWidth: '28px',
125
- padding: vars.spacing.xs,
126
- borderRadius: vars.radii.control,
123
+ minHeight: '24px',
124
+ minWidth: '28px',
125
+ padding: vars.spacing.xs,
126
+ borderRadius: vars.radii.control,
127
+ });
128
+
129
+ globalStyle('.zylem-button-icon', {
130
+ display: 'inline-flex',
131
+ alignItems: 'center',
132
+ justifyContent: 'center',
133
+ flexShrink: 0,
134
+ });
135
+
136
+ globalStyle('.zylem-button svg', {
137
+ width: '16px',
138
+ height: '16px',
139
+ flexShrink: 0,
140
+ stroke: 'currentColor',
141
+ });
142
+
143
+ globalStyle('.zylem-button[data-size="sm"] svg', {
144
+ width: '14px',
145
+ height: '14px',
127
146
  });
@@ -1,5 +1,4 @@
1
- import type { JSX } from 'solid-js';
2
- import { splitProps } from 'solid-js';
1
+ import { Show, splitProps, type JSX } from 'solid-js';
3
2
 
4
3
  export type ButtonVariant = 'default' | 'primary' | 'active' | 'danger' | 'ghost';
5
4
  export type ButtonSize = 'sm' | 'md' | 'lg' | 'icon';
@@ -8,6 +7,8 @@ export interface ButtonProps
8
7
  extends JSX.ButtonHTMLAttributes<HTMLButtonElement> {
9
8
  variant?: ButtonVariant;
10
9
  size?: ButtonSize;
10
+ /** Icon drawn before the label. Omitted, the button is label only. */
11
+ icon?: JSX.Element | undefined;
11
12
  }
12
13
 
13
14
  /**
@@ -19,7 +20,7 @@ export interface ButtonProps
19
20
  * `@zylem/ui/styles.css`), selected via `data-variant` / `data-size`.
20
21
  */
21
22
  export function Button(props: ButtonProps) {
22
- const [local, rest] = splitProps(props, ['variant', 'size', 'class', 'children']);
23
+ const [local, rest] = splitProps(props, ['variant', 'size', 'class', 'children', 'icon']);
23
24
  return (
24
25
  <button
25
26
  type="button"
@@ -28,6 +29,11 @@ export function Button(props: ButtonProps) {
28
29
  data-size={local.size ?? 'md'}
29
30
  {...rest}
30
31
  >
32
+ <Show when={local.icon}>
33
+ <span class="zylem-button-icon" aria-hidden="true">
34
+ {local.icon}
35
+ </span>
36
+ </Show>
31
37
  {local.children}
32
38
  </button>
33
39
  );
@@ -22,7 +22,7 @@ globalStyle('.zylem-callout-title', {
22
22
  });
23
23
 
24
24
  globalStyle('.zylem-callout-message code', {
25
- fontFamily: "'Courier New', monospace",
25
+ fontFamily: vars.typography.fontFamilyMono,
26
26
  fontSize: '0.95em',
27
27
  });
28
28
 
@@ -26,7 +26,7 @@ export function Callout(props: CalloutProps) {
26
26
  <Show when={props.title}>
27
27
  <span class="zylem-callout-title">{props.title}</span>
28
28
  </Show>
29
- <span class="zylem-callout-message">{props.children}</span>
29
+ <div class="zylem-callout-message">{props.children}</div>
30
30
  </div>
31
31
  );
32
32
  }
@@ -64,6 +64,7 @@ globalStyle('.zylem-card-media > img', {
64
64
  });
65
65
 
66
66
  globalStyle('.zylem-card-title', {
67
+ margin: 0,
67
68
  fontSize: vars.typography.fontSize,
68
69
  fontWeight: 700,
69
70
  color: '#F4FAFF',
@@ -1,13 +1,19 @@
1
1
  import { Show, splitProps } from 'solid-js';
2
2
  import type { JSX } from 'solid-js';
3
+ import { Heading, type HeadingLevel } from '../shared/Heading';
3
4
 
4
5
  export interface CardProps
5
6
  extends Omit<JSX.HTMLAttributes<HTMLDivElement>, 'title'> {
6
7
  title?: JSX.Element;
8
+ /** Heading level of the title (default 3). */
9
+ headingLevel?: HeadingLevel;
7
10
  description?: JSX.Element;
8
11
  /** Artwork above the title: a thumbnail, preview swatch, or icon well. */
9
12
  media?: JSX.Element;
10
- /** Renders hover glow + pointer cursor and click affordances. */
13
+ /**
14
+ * Renders hover glow + pointer cursor and click affordances. The card
15
+ * becomes a button, so it must not contain other controls.
16
+ */
11
17
  interactive?: boolean;
12
18
  selected?: boolean;
13
19
  /** Rendered after the description: tags, actions, metadata. */
@@ -22,6 +28,7 @@ export interface CardProps
22
28
  export function Card(props: CardProps) {
23
29
  const [local, rest] = splitProps(props, [
24
30
  'title',
31
+ 'headingLevel',
25
32
  'description',
26
33
  'media',
27
34
  'interactive',
@@ -66,7 +73,9 @@ export function Card(props: CardProps) {
66
73
  <div class="zylem-card-media">{local.media}</div>
67
74
  </Show>
68
75
  <Show when={local.title}>
69
- <div class="zylem-card-title">{local.title}</div>
76
+ <Heading level={local.headingLevel ?? 3} class="zylem-card-title">
77
+ {local.title}
78
+ </Heading>
70
79
  </Show>
71
80
  <Show when={local.description}>
72
81
  <div class="zylem-card-description">{local.description}</div>
@@ -4,6 +4,8 @@ import { Show } from 'solid-js';
4
4
 
5
5
  export interface CheckboxProps {
6
6
  label?: string;
7
+ /** Accessible name when there is no visible `label`. */
8
+ 'aria-label'?: string;
7
9
  checked?: boolean;
8
10
  defaultChecked?: boolean;
9
11
  onChange?: (checked: boolean) => void;
@@ -25,10 +27,10 @@ export function Checkbox(props: CheckboxProps) {
25
27
  disabled={props.disabled}
26
28
  name={props.name}
27
29
  >
28
- <KCheckbox.Input class="zylem-checkbox-input" />
30
+ <KCheckbox.Input class="zylem-checkbox-input" aria-label={props['aria-label']} />
29
31
  <KCheckbox.Control class="zylem-checkbox-control">
30
32
  <KCheckbox.Indicator>
31
- <Check class="zylem-checkbox-icon" />
33
+ <Check class="zylem-checkbox-icon" aria-hidden="true" />
32
34
  </KCheckbox.Indicator>
33
35
  </KCheckbox.Control>
34
36
  <Show when={props.label}>
@@ -50,7 +50,7 @@ export interface ColorPaletteProps {
50
50
  export function ColorPalette(props: ColorPaletteProps) {
51
51
  return (
52
52
  <div
53
- role="listbox"
53
+ role="group"
54
54
  aria-label="Palette"
55
55
  class={props.class ? `zylem-palette ${props.class}` : 'zylem-palette'}
56
56
  >
@@ -35,7 +35,7 @@ globalStyle('.zylem-console', {
35
35
  background: 'rgba(4, 12, 20, 0.72)',
36
36
  boxShadow: vars.effects.shadowInset,
37
37
  color: vars.colors.consoleText,
38
- fontFamily: "'Courier New', monospace",
38
+ fontFamily: vars.typography.fontFamilyMono,
39
39
  fontSize: `calc(${vars.typography.fontSize} - 1px)`,
40
40
  lineHeight: 1.45,
41
41
  outline: 'none',
@@ -9,6 +9,8 @@ export interface ConsoleProps {
9
9
  onInput?: (value: string) => void;
10
10
  onClear?: () => void;
11
11
  readOnly?: boolean;
12
+ /** Accessible name of the text field (default "Console"). */
13
+ label?: string;
12
14
  class?: string;
13
15
  /** Extra action buttons rendered next to Clear. */
14
16
  actions?: JSX.Element;
@@ -30,6 +32,7 @@ export function Console(props: ConsoleProps) {
30
32
  <div class="zylem-console-wrapper">
31
33
  <textarea
32
34
  class="zylem-console zylem-scroll"
35
+ aria-label={props.label ?? 'Console'}
33
36
  value={props.value}
34
37
  spellcheck={false}
35
38
  readOnly={props.readOnly || !props.onInput}
@@ -1,7 +1,8 @@
1
1
  import { globalStyle, keyframes } from '@vanilla-extract/css';
2
+ import { LAYER_PORTAL_CLASS } from '../../layers/resolve-mount';
2
3
  import { vars } from '../../theme.css';
3
4
 
4
- const overlayEnter = keyframes({
5
+ const backdropEnter = keyframes({
5
6
  from: { opacity: 0 },
6
7
  to: { opacity: 1 },
7
8
  });
@@ -11,26 +12,56 @@ const contentEnter = keyframes({
11
12
  to: { opacity: 1, transform: 'translateY(0) scale(1)' },
12
13
  });
13
14
 
14
- globalStyle('.zylem-dialog-overlay', {
15
+ // The <dialog> is a transparent sheet over the whole viewport; the card inside
16
+ // is the visible window. Undoes the UA's `dialog:modal` box (centered,
17
+ // max-sized, scrolling) but never sets `display`, which would override the UA
18
+ // rule that hides a closed dialog.
19
+ globalStyle('.zylem-dialog', {
15
20
  position: 'fixed',
16
21
  inset: 0,
17
- zIndex: vars.layers.modal,
22
+ width: '100%',
23
+ height: '100%',
24
+ maxWidth: 'none',
25
+ maxHeight: 'none',
26
+ margin: 0,
27
+ padding: 0,
28
+ border: 0,
29
+ background: 'transparent',
30
+ color: 'inherit',
31
+ font: 'inherit',
32
+ overflow: 'visible',
33
+ outline: 'none',
34
+ });
35
+
36
+ globalStyle('.zylem-dialog::backdrop', {
18
37
  background: 'rgba(5, 10, 18, 0.6)',
19
38
  backdropFilter: 'blur(3px)',
20
- animation: `${overlayEnter} 0.15s ${vars.motion.easeOut}`,
39
+ animation: `${backdropEnter} 0.15s ${vars.motion.easeOut}`,
40
+ });
41
+
42
+ globalStyle('html:has(.zylem-dialog[open])', {
43
+ overflow: 'hidden',
21
44
  });
22
45
 
23
46
  globalStyle('.zylem-dialog-positioner', {
24
- position: 'fixed',
25
- inset: 0,
26
- // One above the backdrop it shares a tier with.
27
- zIndex: `calc(${vars.layers.modal} + 1)`,
28
47
  display: 'flex',
29
48
  alignItems: 'center',
30
49
  justifyContent: 'center',
50
+ boxSizing: 'border-box',
51
+ width: '100%',
52
+ height: '100%',
31
53
  padding: vars.spacing.lg,
32
54
  });
33
55
 
56
+ // The scope's portal container, where overlays opened from the dialog mount.
57
+ // Out of flow so it takes no part in centering the card, at the dialog's
58
+ // origin, which is the viewport's.
59
+ globalStyle(`.zylem-dialog-positioner > .${LAYER_PORTAL_CLASS}`, {
60
+ position: 'absolute',
61
+ top: 0,
62
+ left: 0,
63
+ });
64
+
34
65
  globalStyle('.zylem-dialog-content', {
35
66
  display: 'flex',
36
67
  flexDirection: 'column',
@@ -124,20 +155,13 @@ globalStyle('.zylem-dialog-body', {
124
155
  lineHeight: 1.45,
125
156
  });
126
157
 
127
- // In fullscreen the body and its layer scope become flex columns so content
128
- // can lay out against the dialog's height (and own its scrolling).
158
+ // In fullscreen the body becomes a flex column so content can lay out against
159
+ // the dialog's height (and own its scrolling).
129
160
  globalStyle('.zylem-dialog-content[data-size="fullscreen"] .zylem-dialog-body', {
130
161
  display: 'flex',
131
162
  flexDirection: 'column',
132
163
  });
133
164
 
134
- globalStyle('.zylem-dialog-content[data-size="fullscreen"] .zylem-dialog-scope', {
135
- display: 'flex',
136
- flexDirection: 'column',
137
- flex: '1 1 auto',
138
- minHeight: 0,
139
- });
140
-
141
165
  globalStyle('.zylem-dialog-footer', {
142
166
  flex: 'none',
143
167
  display: 'flex',