@fusion.dev/helix 0.142.1 → 0.143.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 (83) hide show
  1. package/README.md +9 -5
  2. package/custom-elements.json +19275 -1410
  3. package/dist/accordion/item/accordion-item.css.js +6 -1
  4. package/dist/accordion/item/accordion-item.d.ts +1 -0
  5. package/dist/announcement/root/announcement-root.d.ts +3 -3
  6. package/dist/app-header/root/app-header-root.d.ts +1 -3
  7. package/dist/aura/shader.d.ts +5 -1
  8. package/dist/aura/shader.js +88 -613
  9. package/dist/avatar/root/avatar-root.d.ts +3 -3
  10. package/dist/badge/root/badge-root.css.js +3 -2
  11. package/dist/badge/root/badge-root.d.ts +1 -0
  12. package/dist/bar-chart/root/bar-chart-root.css.js +10 -0
  13. package/dist/blockquote/root/blockquote-root.css.js +4 -0
  14. package/dist/card/root/card-root.css.js +1 -0
  15. package/dist/carousel/root/carousel-root.js +2 -1
  16. package/dist/checkbox/root/checkbox-root.css.js +3 -3
  17. package/dist/color-picker/root/color-picker-root.d.ts +0 -1
  18. package/dist/color-picker/root/color-picker-root.js +0 -1
  19. package/dist/combobox/root/combobox-root.css.js +5 -4
  20. package/dist/data-table/root/data-table-root.css.js +7 -0
  21. package/dist/date-picker/root/date-picker-root.css.js +15 -3
  22. package/dist/donut-chart/root/donut-chart-root.css.js +10 -0
  23. package/dist/drawer/root/drawer-root.css.js +25 -4
  24. package/dist/element-events.generated.js +41 -10
  25. package/dist/file-input/root/file-input-root.d.ts +0 -1
  26. package/dist/file-input/root/file-input-root.js +0 -1
  27. package/dist/filter-input/root/filter-input-root.d.ts +0 -1
  28. package/dist/filter-input/root/filter-input-root.js +0 -1
  29. package/dist/grid/root/grid-root.d.ts +15 -11
  30. package/dist/grid/root/grid-root.js +49 -15
  31. package/dist/icon-button/root/icon-button-root.css.js +43 -16
  32. package/dist/icon-button/root/icon-button-root.d.ts +4 -1
  33. package/dist/image/root/image-root.css.js +1 -1
  34. package/dist/input/root/input-root.d.ts +0 -1
  35. package/dist/input/root/input-root.js +0 -1
  36. package/dist/line-chart/root/line-chart-root.css.js +10 -0
  37. package/dist/list/item/list-item.css.js +7 -1
  38. package/dist/loader/root/loader-root.d.ts +7 -4
  39. package/dist/menu/item/menu-item.css.js +1 -1
  40. package/dist/menu/root/menu-root.d.ts +0 -3
  41. package/dist/meter/root/meter-root.css.js +4 -4
  42. package/dist/number-input/root/number-input-root.d.ts +3 -0
  43. package/dist/orb/root/orb-root.d.ts +11 -2
  44. package/dist/orb/root/orb-root.js +5 -1
  45. package/dist/qr-code/root/qr-code-root.css.js +2 -1
  46. package/dist/qr-code/root/qr-code-root.d.ts +2 -2
  47. package/dist/rating/root/rating-root.d.ts +3 -0
  48. package/dist/sandbox/index.d.ts +2 -2
  49. package/dist/sandbox/root/sandbox-root.d.ts +20 -1
  50. package/dist/sandbox/root/sandbox-root.js +27 -10
  51. package/dist/sandbox/source/sandbox-source.d.ts +6 -1
  52. package/dist/sandbox/source/sandbox-source.js +13 -3
  53. package/dist/sandbox/terminal/sandbox-terminal.d.ts +3 -1
  54. package/dist/sandbox/terminal/sandbox-terminal.js +3 -1
  55. package/dist/skeleton/root/skeleton-root.css.js +4 -0
  56. package/dist/skeleton/root/skeleton-root.d.ts +4 -1
  57. package/dist/table/column/table-column.css.js +5 -0
  58. package/dist/tabs/panel/tab-panel.css.js +1 -1
  59. package/dist/tabs/panel/tab-panel.d.ts +2 -0
  60. package/dist/tabs/root/tabs-root.css.js +13 -7
  61. package/dist/tabs/root/tabs-root.d.ts +5 -0
  62. package/dist/tabs/tab/tab.css.js +4 -4
  63. package/dist/tabs/tab/tab.d.ts +5 -0
  64. package/dist/tag/root/tag-root.d.ts +2 -3
  65. package/dist/text/root/text-root.css.js +58 -1
  66. package/dist/text/root/text-root.d.ts +11 -2
  67. package/dist/text/root/text-root.js +19 -1
  68. package/dist/time-input/root/time-input-root.d.ts +0 -1
  69. package/dist/time-input/root/time-input-root.js +0 -1
  70. package/dist/toast/root/toast-root.css.js +31 -7
  71. package/dist/toast/root/toast-root.d.ts +6 -9
  72. package/dist/toggle/group/toggle-group.d.ts +3 -0
  73. package/dist/tour/stop/tour-stop.css.js +5 -0
  74. package/dist/trend/root/trend-root.d.ts +0 -1
  75. package/dist/trend/root/trend-root.js +0 -1
  76. package/dist/video/root/video-root.d.ts +0 -1
  77. package/dist/video/root/video-root.js +0 -1
  78. package/dist/video-playlist/menu/video-playlist-menu.d.ts +0 -1
  79. package/dist/video-playlist/menu/video-playlist-menu.js +0 -1
  80. package/lint.json +1 -1
  81. package/package.json +6 -6
  82. package/types/jsx.d.ts +4973 -1964
  83. package/types/vue.d.ts +998 -212
@@ -58,7 +58,7 @@ var(--helix-color-foreground)
58
58
 
59
59
  [part="button"] {
60
60
  align-items: center;
61
- background-color: transparent;
61
+ background-color: var(--icon-button-background-color, transparent);
62
62
  block-size: var(--_icon-button-size);
63
63
  border: var(--helix-border-thin) solid transparent;
64
64
 
@@ -75,7 +75,7 @@ border-start-start-radius: var(
75
75
  --button-radius-start,
76
76
  var(--icon-button-radius, var(--helix-radii-sm))
77
77
  );
78
- color: var(--_icon-button-ink);
78
+ color: var(--icon-button-foreground-color, var(--_icon-button-ink));
79
79
  cursor: pointer;
80
80
  display: inline-flex;
81
81
 
@@ -89,8 +89,14 @@ color var(--helix-duration-fast) var(--helix-easing-standard);
89
89
  }
90
90
 
91
91
  :host(:not(:disabled)) [part="button"]:hover {
92
- background-color: color-mix(in oklab, var(--_icon-button-tone) 8%, transparent);
93
- color: var(--_icon-button-ink-hover);
92
+ background-color: var(
93
+ --icon-button-background-color-hover,
94
+ var(
95
+ --icon-button-background-color,
96
+ color-mix(in oklab, var(--_icon-button-tone) 8%, transparent)
97
+ )
98
+ );
99
+ color: var(--icon-button-foreground-color, var(--_icon-button-ink-hover));
94
100
  }
95
101
 
96
102
  :host([shape="circle"]) [part="button"] {
@@ -115,34 +121,55 @@ outline-offset: var(--helix-size-focus-offset);
115
121
  }
116
122
 
117
123
  :host([emphasis="solid"]) [part="button"] {
118
- background-color: var(--_icon-button-fill);
124
+ background-color: var(--icon-button-background-color, var(--_icon-button-fill));
119
125
  border-color: transparent;
120
- color: var(--_icon-button-on-tone);
126
+ color: var(--icon-button-foreground-color, var(--_icon-button-on-tone));
121
127
  }
122
128
  :host(:not(:disabled)[emphasis="solid"]) [part="button"]:hover {
123
- background-color: var(--_icon-button-fill-hover);
124
- color: var(--_icon-button-on-tone);
129
+ background-color: var(
130
+ --icon-button-background-color-hover,
131
+ var(--icon-button-background-color, var(--_icon-button-fill-hover))
132
+ );
133
+ color: var(--icon-button-foreground-color, var(--_icon-button-on-tone));
125
134
  }
126
135
 
127
136
  :host([emphasis="outlined"]) [part="button"] {
128
- background-color: color-mix(in oklab, var(--_icon-button-tone) 6%, transparent);
137
+ background-color: var(
138
+ --icon-button-background-color,
139
+ color-mix(in oklab, var(--_icon-button-tone) 6%, transparent)
140
+ );
129
141
  border-color: var(--_icon-button-edge);
130
- color: var(--_icon-button-tone);
142
+ color: var(--icon-button-foreground-color, var(--_icon-button-tone));
131
143
  }
132
144
  :host(:not(:disabled)[emphasis="outlined"]) [part="button"]:hover {
133
- background-color: color-mix(in oklab, var(--_icon-button-tone) 12%, transparent);
134
- color: var(--_icon-button-tone);
145
+ background-color: var(
146
+ --icon-button-background-color-hover,
147
+ var(
148
+ --icon-button-background-color,
149
+ color-mix(in oklab, var(--_icon-button-tone) 12%, transparent)
150
+ )
151
+ );
152
+ color: var(--icon-button-foreground-color, var(--_icon-button-tone));
135
153
  }
136
154
 
137
155
  :host([emphasis="subtle"]) [part="button"] {
138
- background-color: color-mix(in oklab, var(--_icon-button-tone) 14%, transparent);
156
+ background-color: var(
157
+ --icon-button-background-color,
158
+ color-mix(in oklab, var(--_icon-button-tone) 14%, transparent)
159
+ );
139
160
  border-color: transparent;
140
- color: var(--_icon-button-tone);
161
+ color: var(--icon-button-foreground-color, var(--_icon-button-tone));
141
162
  }
142
163
 
143
164
  :host(:not(:disabled)[emphasis="subtle"]) [part="button"]:hover {
144
- background-color: color-mix(in oklab, var(--_icon-button-tone) 22%, transparent);
145
- color: var(--_icon-button-tone);
165
+ background-color: var(
166
+ --icon-button-background-color-hover,
167
+ var(
168
+ --icon-button-background-color,
169
+ color-mix(in oklab, var(--_icon-button-tone) 22%, transparent)
170
+ )
171
+ );
172
+ color: var(--icon-button-foreground-color, var(--_icon-button-tone));
146
173
  }
147
174
 
148
175
  :host(:disabled) [part="button"] {
@@ -21,8 +21,11 @@ declare const HelixIconButton_base: (new (...args: any[]) => import("../../share
21
21
  *
22
22
  * @slot - The icon.
23
23
  *
24
- * @csspart button - The inner button or anchor.
24
+ * @csspart button - The inner button.
25
25
  *
26
+ * @cssproperty --icon-button-background-color - Fill behind the button, in every state.
27
+ * @cssproperty --icon-button-background-color-hover - Fill behind the button under the pointer. Without it, `--icon-button-background-color` also applies on hover.
28
+ * @cssproperty --icon-button-foreground-color - Icon color.
26
29
  * @cssproperty --icon-button-radius - Corner radius of the control, which a group flattens through `--button-radius-start` and `--button-radius-end`.
27
30
  * @cssproperty --icon-button-shadow - Shadow of a raised icon button.
28
31
  * @cssproperty --icon-button-size - Edge length of the control.
@@ -19,7 +19,7 @@ object-position: var(--image-position, center);
19
19
  }
20
20
 
21
21
  :host([data-status="error"]) [part="image"] {
22
- visibility: hidden;
22
+ opacity: 0;
23
23
  }
24
24
 
25
25
  [part="placeholder"],
@@ -1,6 +1,5 @@
1
1
  import { HelixHeadlessInput } from '@fusion.dev/helix-headless/input';
2
2
  import type { CSSResultGroup } from 'lit';
3
- import '../../icon/index.js';
4
3
  /**
5
4
  * @summary A field for a single line of text.
6
5
  *
@@ -6,7 +6,6 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
6
6
  };
7
7
  import { customElement } from '@fusion.dev/helix-core';
8
8
  import { HelixHeadlessInput } from '@fusion.dev/helix-headless/input';
9
- import '../../icon/index.js';
10
9
  import { styles as fieldStyles } from '../../shared/field/field.css.js';
11
10
  import { styles as fieldFrameStyles } from '../../shared/field/field-frame.css.js';
12
11
  import { styles as fieldStackedStyles } from '../../shared/field/field-stacked.css.js';
@@ -144,4 +144,14 @@ stroke-opacity: calc(0.62 * var(--helix-material-opacity));
144
144
 
145
145
  :host([material="frosted"]) [part="line"] {
146
146
  stroke-opacity: calc(0.88 * var(--helix-material-opacity));
147
+ }
148
+
149
+ [part="summary"] {
150
+ block-size: 1px;
151
+ clip-path: inset(50%);
152
+ inline-size: 1px;
153
+ margin: var(--helix-space-none);
154
+ overflow: hidden;
155
+ position: absolute;
156
+ white-space: nowrap;
147
157
  }`);
@@ -63,7 +63,13 @@ grid-column: 4;
63
63
  grid-row: 1 / span 2;
64
64
  }
65
65
 
66
- a[part="row"] {
66
+ [part="link"] {
67
+ align-items: center;
67
68
  color: inherit;
69
+ display: grid;
70
+ grid-column: 1 / 4;
71
+ grid-row: 1 / span 2;
72
+ grid-template-columns: subgrid;
73
+ grid-template-rows: subgrid;
68
74
  text-decoration: none;
69
75
  }`);
@@ -4,8 +4,11 @@ import type { CSSResultGroup } from 'lit';
4
4
  * @summary Shows that work of unknown length is in progress.
5
5
  *
6
6
  * @description
7
- * Sized in `em`, so it matches the text beside it. The color is `currentColor`,
8
- * so it matches the text color around it, including inside a primary button.
7
+ * Sized from the loader size tokens, so a loader is the same size wherever it
8
+ * is placed and follows the density region it sits in. Set `--loader-size:
9
+ * 1em` to match the text beside it, as inside a button. The color is
10
+ * `currentColor`, so it matches the text color around it, including inside a
11
+ * primary button.
9
12
  *
10
13
  * @since 0.1.0
11
14
  * @status stable
@@ -14,12 +17,12 @@ import type { CSSResultGroup } from 'lit';
14
17
  * @documentation https://fusion.dev/helix/components/loader
15
18
  *
16
19
  * @cssproperty --loader-color - Color of the moving arc.
17
- * @cssproperty --loader-size - Diameter. Defaults to 1.5em.
20
+ * @cssproperty --loader-size - Diameter. Defaults to the size token that `size` picks, which is 1.5rem at the default density.
18
21
  * @cssproperty --loader-track-color - Color of the circle behind the arc.
19
22
  */
20
23
  export declare class HelixLoader extends HelixHeadlessLoader {
21
24
  /**
22
- * Diameter relative to the surrounding text.
25
+ * Diameter, from the loader size tokens.
23
26
  */
24
27
  size?: 'sm' | 'md' | 'lg';
25
28
  /**
@@ -90,7 +90,7 @@ svg[part="mark"] {
90
90
  color: var(--helix-color-foreground-subtle);
91
91
  }
92
92
 
93
- :host([checked]) svg[part="mark"] {
93
+ :host(:state(checked)) svg[part="mark"] {
94
94
  color: var(--helix-color-foreground);
95
95
  }
96
96
 
@@ -19,12 +19,9 @@ import type { CSSResultGroup } from 'lit';
19
19
  * @cssproperty --menu-background-color - Fill of the surface.
20
20
  * @cssproperty --menu-border-color - Color of the panel's edge.
21
21
  * @cssproperty --menu-border-width - Width of the panel's edge. Defaults to `--helix-border-floating`, which is `0`, so the panel stands out by its shadow unless a theme sets a border.
22
- * `--helix-border-floating`, which is `0`, so the panel is told apart by its lift unless a
23
- * theme says otherwise.
24
22
  * @cssproperty --menu-max-block-size - The tallest the menu grows before it scrolls.
25
23
  * @cssproperty --menu-min-inline-size - The narrowest the menu gets.
26
24
  * @cssproperty --menu-shadow - Shadow under the panel. Defaults to `--helix-shadow-floating`, so the panel keeps a shadow over any content below it.
27
- * floating surface sits over content nobody can predict, so it keeps a lift by default.
28
25
  */
29
26
  export declare class HelixMenu extends HelixHeadlessMenu {
30
27
  static styles: CSSResultGroup;
@@ -131,7 +131,7 @@ stroke-dasharray: var(--meter-sweep) 100;
131
131
  :host([shape="arc"]) [part~="indicator"],
132
132
  :host([shape="ring"]) [part~="indicator"] {
133
133
 
134
- --_meter-hue: var(--helix-color-foreground-muted);
134
+ --_meter-hue: var(--meter-color, var(--helix-color-foreground-muted));
135
135
  scale: none;
136
136
  stroke: var(--_meter-hue);
137
137
  stroke-dasharray: var(--meter-along) 100;
@@ -151,19 +151,19 @@ stroke-dashoffset: calc(var(--meter-along) * -1);
151
151
 
152
152
  :host([shape="arc"]) [part~="indicator"][data-band="good"],
153
153
  :host([shape="ring"]) [part~="indicator"][data-band="good"] {
154
- --_meter-hue: var(--helix-color-success);
154
+ --_meter-hue: var(--meter-color, var(--helix-color-success));
155
155
  stroke: var(--_meter-hue);
156
156
  }
157
157
 
158
158
  :host([shape="arc"]) [part~="indicator"][data-band="fair"],
159
159
  :host([shape="ring"]) [part~="indicator"][data-band="fair"] {
160
- --_meter-hue: var(--helix-color-warning);
160
+ --_meter-hue: var(--meter-color, var(--helix-color-warning));
161
161
  stroke: var(--_meter-hue);
162
162
  }
163
163
 
164
164
  :host([shape="arc"]) [part~="indicator"][data-band="poor"],
165
165
  :host([shape="ring"]) [part~="indicator"][data-band="poor"] {
166
- --_meter-hue: var(--helix-color-danger);
166
+ --_meter-hue: var(--meter-color, var(--helix-color-danger));
167
167
  stroke: var(--_meter-hue);
168
168
  }
169
169
 
@@ -22,6 +22,9 @@ import type { CSSResultGroup } from 'lit';
22
22
  * @documentation https://fusion.dev/helix/components/number-input
23
23
  *
24
24
  * @slot end - Content after the number, such as a unit.
25
+ * @slot feedback - The message that says what is wrong, for content richer than a string. It shows while there is a message.
26
+ * @slot hint - Help under the control, for content richer than the `hint` text.
27
+ * @slot label - The label, for content richer than the `label` text.
25
28
  * @slot start - Content before the number, such as a unit or an icon.
26
29
  *
27
30
  * @csspart control - The input that holds the number.
@@ -31,8 +31,10 @@ import { type OrbState } from '../shapes.js';
31
31
  * `prefers-reduced-motion`, it draws one still frame.
32
32
  *
33
33
  * The orb is styled only and has behavior, which the tier split does not
34
- * otherwise allow. The behavior is drawing dots to a canvas, so there is no
35
- * structure to restyle and nothing for a headless element to hold.
34
+ * otherwise allow. Its role, name and busy state follow `state`, and the rest
35
+ * is drawing dots to a canvas, so there is no structure to restyle. For a
36
+ * drawing of your own, the headless `Loader` holds the same busy progress bar
37
+ * and its name.
36
38
  *
37
39
  * @since 0.8.0
38
40
  * @status stable
@@ -85,6 +87,13 @@ export declare class HelixOrb extends HelixElement {
85
87
  * markup.
86
88
  */
87
89
  state: OrbState;
90
+ /**
91
+ * The word for each state, for a page in another language.
92
+ *
93
+ * A state left out keeps its English word, and `label` wins over both. It is
94
+ * a property with no attribute, keyed by state, such as `{ thinking: '…' }`.
95
+ */
96
+ stateLabels: Partial<Record<OrbState, string>>;
88
97
  private canvas;
89
98
  private context;
90
99
  /**
@@ -28,6 +28,7 @@ let HelixOrb = class HelixOrb extends HelixElement {
28
28
  super(...arguments);
29
29
  this.label = '';
30
30
  this.state = 'thinking';
31
+ this.stateLabels = {};
31
32
  this.context = null;
32
33
  this.gate = new PaintGateController(this, {
33
34
  canRun: () => !!this.context && !this.lost,
@@ -73,7 +74,7 @@ let HelixOrb = class HelixOrb extends HelixElement {
73
74
  ? this.state
74
75
  : 'thinking';
75
76
  hostAria(this.internals, {
76
- label: this.label || LABELS[state],
77
+ label: this.label || this.stateLabels[state] || LABELS[state],
77
78
  role: state === 'done' ? 'img' : 'progressbar',
78
79
  });
79
80
  this.internals.ariaBusy = state === 'done' ? 'false' : 'true';
@@ -244,6 +245,9 @@ __decorate([
244
245
  __decorate([
245
246
  property()
246
247
  ], HelixOrb.prototype, "state", void 0);
248
+ __decorate([
249
+ property({ attribute: false })
250
+ ], HelixOrb.prototype, "stateLabels", void 0);
247
251
  HelixOrb = __decorate([
248
252
  customElement('helix-orb')
249
253
  ], HelixOrb);
@@ -23,7 +23,8 @@ fill: var(--qr-code-finder-color, currentColor);
23
23
 
24
24
  [part="logo"] {
25
25
  align-items: center;
26
- background-color: var(--qr-code-background-color, var(--helix-color-surface-raised));
26
+
27
+ background-color: var(--qr-code-background-color, var(--helix-color-foreground-media));
27
28
  border-radius: var(--helix-radii-sm);
28
29
 
29
30
  box-sizing: border-box;
@@ -17,8 +17,8 @@ import type { CSSResultGroup } from 'lit';
17
17
  * @category media
18
18
  * @documentation https://fusion.dev/helix/components/qr-code
19
19
  *
20
- * @cssproperty --qr-code-background-color - The fill behind the modules, including the quiet zone.
21
- * @cssproperty --qr-code-color - Color of the dark modules. Defaults to the page's text color.
20
+ * @cssproperty --qr-code-background-color - The fill behind the modules, including the quiet zone and the logo's box.
21
+ * @cssproperty --qr-code-color - Color of the dark modules. Defaults to the dark media ink, which is the same in both color schemes so the code scans.
22
22
  * @cssproperty --qr-code-finder-color - Color of the three corner squares. Defaults
23
23
  * to the color of the modules.
24
24
  * @cssproperty --qr-code-logo-padding - The clear space around the center logo.
@@ -13,6 +13,9 @@ import type { CSSResultGroup } from 'lit';
13
13
  * @aliases Stars, Score, Review score
14
14
  * @documentation https://fusion.dev/helix/components/rating
15
15
  *
16
+ * @slot feedback - The message that says what is wrong, for content richer than a string. It shows while there is a message.
17
+ * @slot hint - Help under the control, for content richer than the `hint` text.
18
+ * @slot label - The label, for content richer than the `label` text.
16
19
  * @slot mark - The shape of a mark, copied into every position. When the slot is
17
20
  * empty, the stylesheet draws a mark, and `--rating-mark` changes its shape.
18
21
  *
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * The sandbox: a preview that runs what it is given, and the panes around it.
3
3
  *
4
- * The editor is deliberately absent and lives on in `@fusion.dev/helix-editor`, because
5
- * it carries CodeMirror. That is roughly 4 MB installed against a design system
4
+ * The editor is deliberately absent and lives in `@fusion.dev/helix-sandbox`, which
5
+ * is private, because it carries CodeMirror. That is roughly 4 MB installed against a design system
6
6
  * of about the same size, and a `dependencies` entry is paid at install time by
7
7
  * every consumer whether or not they import it, which is the one cost a subpath
8
8
  * export cannot avoid. Its pane takes an editor by slot, so bringing one is a
@@ -127,10 +127,14 @@ export declare class HelixSandbox extends HelixElement {
127
127
  panelSize: number;
128
128
  /**
129
129
  * The name of the divider between the panes, read by screen readers.
130
+ *
131
+ * Empty names the lead pane, "Resize source pane" or "Resize preview pane",
132
+ * since the divider's value is that pane's share.
130
133
  */
131
134
  separatorLabel: string;
132
135
  /**
133
- * Percentage of the sandbox given to the source pane.
136
+ * Percentage of the sandbox given to the lead pane: the source, unless `lead`
137
+ * is `preview`.
134
138
  */
135
139
  split: number;
136
140
  /**
@@ -212,6 +216,21 @@ export declare class HelixSandbox extends HelixElement {
212
216
  * sandbox hears a great deal that is not its source.
213
217
  */
214
218
  private readonly handleSourceChange;
219
+ /**
220
+ * Writes a pane's text into every preview here.
221
+ *
222
+ * `seeding` writes only into a preview that has nothing yet, so a preview
223
+ * given its own `source` keeps it until the first edit.
224
+ */
225
+ private write;
226
+ /**
227
+ * Hands what the source panes already hold to the previews.
228
+ *
229
+ * Only edits were forwarded, so a pane written with a `value` showed its
230
+ * text beside an empty preview until the first keystroke, and the text had
231
+ * to be written twice. Client only, like the other reads of the light DOM.
232
+ */
233
+ private readonly seed;
215
234
  /**
216
235
  * Writes the cursor position onto any status bar slotted here.
217
236
  *
@@ -22,7 +22,7 @@ let HelixSandbox = class HelixSandbox extends HelixElement {
22
22
  this.panelCollapsedSize = 0;
23
23
  this.panelLabel = 'Resize panel';
24
24
  this.panelSize = DEFAULT_PANEL;
25
- this.separatorLabel = 'Resize source pane';
25
+ this.separatorLabel = '';
26
26
  this.split = DEFAULT_SPLIT;
27
27
  this.hasPanel = false;
28
28
  this.bodyHeight = 0;
@@ -37,15 +37,23 @@ let HelixSandbox = class HelixSandbox extends HelixElement {
37
37
  }
38
38
  const { value } = event.detail;
39
39
  const pane = event.composedPath()[0];
40
- if (pane?.name !== 'css' && pane?.name !== 'html') {
40
+ const name = pane?.name;
41
+ if (name !== 'css' && name !== 'html') {
41
42
  return;
42
43
  }
43
- for (const preview of this.previews) {
44
- if (pane.name === 'css') {
45
- preview.styles = value;
44
+ queueMicrotask(() => {
45
+ if (!event.defaultPrevented) {
46
+ this.write(name, value, false);
46
47
  }
47
- else {
48
- preview.source = value;
48
+ });
49
+ };
50
+ this.seed = () => {
51
+ const slot = this.renderRoot.querySelector('slot[name="source"]');
52
+ for (const pane of slot?.assignedElements({ flatten: true }) ?? []) {
53
+ const { name, value } = pane;
54
+ const text = typeof value === 'string' ? value : pane.getAttribute('value');
55
+ if ((name === 'css' || name === 'html') && text) {
56
+ this.write(name, text, true);
49
57
  }
50
58
  }
51
59
  };
@@ -132,6 +140,14 @@ let HelixSandbox = class HelixSandbox extends HelixElement {
132
140
  const slot = this.renderRoot.querySelector('slot[name="source"]');
133
141
  return (slot?.assignedElements({ flatten: true }) ?? []).some((pane) => pane === target || pane.contains(target));
134
142
  }
143
+ write(name, value, seeding) {
144
+ for (const preview of this.previews) {
145
+ const key = name === 'css' ? 'styles' : 'source';
146
+ if (!seeding || !preview[key]) {
147
+ preview[key] = value;
148
+ }
149
+ }
150
+ }
135
151
  parts(selector) {
136
152
  return [...this.querySelectorAll(selector)];
137
153
  }
@@ -213,6 +229,7 @@ let HelixSandbox = class HelixSandbox extends HelixElement {
213
229
  this.finishHydration();
214
230
  }
215
231
  this.watchBody(this.watch === undefined);
232
+ this.seed();
216
233
  }
217
234
  disconnectedCallback() {
218
235
  super.disconnectedCallback();
@@ -246,15 +263,15 @@ let HelixSandbox = class HelixSandbox extends HelixElement {
246
263
  part="panes"
247
264
  exportparts="separator"
248
265
  default-position=${Number.isFinite(this.defaultSplit) ? this.defaultSplit : DEFAULT_SPLIT}
249
- label=${this.separatorLabel}
266
+ label=${this.separatorLabel || (first === 'preview' ? 'Resize preview pane' : 'Resize source pane')}
250
267
  orientation=${vertical ? 'vertical' : 'horizontal'}
251
268
  .position=${split}
252
269
  @helix-before-collapse=${this.handleCollapse}
253
270
  @helix-before-resize=${this.handlePanesChange}
254
271
  >
255
272
  ${''}
256
- <div part="pane" data-pane=${first} slot="start"><slot name=${first}></slot></div>
257
- <div part="pane" data-pane=${second} slot="end"><slot name=${second}></slot></div>
273
+ <div part="pane" data-pane=${first} slot="start"><slot name=${first} @slotchange=${this.seed}></slot></div>
274
+ <div part="pane" data-pane=${second} slot="end"><slot name=${second} @slotchange=${this.seed}></slot></div>
258
275
  </helix-splitter>
259
276
  <div part="panel" slot="end"><slot name="panel" @slotchange=${this.readPanel}></slot></div>
260
277
  </helix-splitter>
@@ -8,7 +8,8 @@ import type { CSSResultGroup, TemplateResult } from 'lit';
8
8
  * keyboards, and screen reader support come from the platform.
9
9
  *
10
10
  * Tab inserts two spaces, so you can indent code. To move focus out of the
11
- * field, press Escape and then Tab.
11
+ * field, press Escape and then Tab. Shift+Tab moves focus back as it does
12
+ * anywhere else. The field's description says so to a screen reader.
12
13
  *
13
14
  * @since 0.1.0
14
15
  * @status stable
@@ -28,6 +29,10 @@ export declare class HelixSandboxSource extends HelixElement {
28
29
  * The editable region's name, read by screen readers.
29
30
  */
30
31
  fieldLabel: string;
32
+ /**
33
+ * The field's description, read by screen readers while Tab indents.
34
+ */
35
+ keysDescription: string;
31
36
  /**
32
37
  * Which source this pane holds. Every `helix-before-edit` event includes it.
33
38
  *
@@ -5,13 +5,15 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
7
  import { customElement, HelixBeforeEditEvent, HelixCaretEvent, HelixElement, onUnlessFalse, restoreValue, } from '@fusion.dev/helix-core';
8
- import { html } from 'lit';
8
+ import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { styles } from './sandbox-source.css.js';
11
+ const MODIFIERS = new Set(['Alt', 'AltGraph', 'Control', 'Meta', 'Shift']);
11
12
  let HelixSandboxSource = class HelixSandboxSource extends HelixElement {
12
13
  constructor() {
13
14
  super(...arguments);
14
15
  this.fieldLabel = 'Example source';
16
+ this.keysDescription = 'Tab inserts two spaces. To leave, press Escape and then Tab.';
15
17
  this.name = 'html';
16
18
  this.tabIndents = true;
17
19
  this.value = '';
@@ -35,7 +37,10 @@ let HelixSandboxSource = class HelixSandboxSource extends HelixElement {
35
37
  this.escaped = true;
36
38
  return;
37
39
  }
38
- if (event.key !== 'Tab' || !this.tabIndents || this.escaped) {
40
+ if (MODIFIERS.has(event.key)) {
41
+ return;
42
+ }
43
+ if (event.key !== 'Tab' || event.shiftKey || !this.tabIndents || this.escaped) {
39
44
  this.escaped = false;
40
45
  return;
41
46
  }
@@ -59,6 +64,7 @@ let HelixSandboxSource = class HelixSandboxSource extends HelixElement {
59
64
  return html `<textarea
60
65
  part="source"
61
66
  aria-label=${this.fieldLabel}
67
+ aria-describedby=${this.tabIndents ? 'keys' : nothing}
62
68
  spellcheck="false"
63
69
  .value=${this.value}
64
70
  @focus=${this.reportCaret}
@@ -66,13 +72,17 @@ let HelixSandboxSource = class HelixSandboxSource extends HelixElement {
66
72
  @keydown=${this.handleKeydown}
67
73
  @keyup=${this.reportCaret}
68
74
  @pointerup=${this.reportCaret}
69
- ></textarea>`;
75
+ ></textarea>
76
+ <span id="keys" hidden>${this.keysDescription}</span>`;
70
77
  }
71
78
  static { this.styles = styles; }
72
79
  };
73
80
  __decorate([
74
81
  property({ attribute: 'field-label' })
75
82
  ], HelixSandboxSource.prototype, "fieldLabel", void 0);
83
+ __decorate([
84
+ property({ attribute: 'keys-description' })
85
+ ], HelixSandboxSource.prototype, "keysDescription", void 0);
76
86
  __decorate([
77
87
  property()
78
88
  ], HelixSandboxSource.prototype, "name", void 0);
@@ -13,7 +13,9 @@ import type { CSSResultGroup, TemplateResult } from 'lit';
13
13
  * warning. Only consecutive repeats collapse, so the order of messages is kept.
14
14
  *
15
15
  * The panel has `role="log"`, the role for an append-only stream. Screen
16
- * readers announce new output without reading the whole panel again.
16
+ * readers announce new output without reading the whole panel again. A line's
17
+ * level is read before it, except a plain `log`, so an error does not sound
18
+ * like any other line.
17
19
  *
18
20
  * @since 0.1.0
19
21
  * @status stable
@@ -49,7 +49,9 @@ let HelixSandboxTerminal = class HelixSandboxTerminal extends HelixElement {
49
49
  ${this.entries.length === 0
50
50
  ? html `<p part="empty">${this.emptyLabel}</p>`
51
51
  : this.entries.map((entry) => html `<div part="entry" data-level=${entry.level}>
52
- <span part="level" aria-hidden="true">${entry.level}</span>
52
+ <span part="level" aria-hidden=${entry.level === 'log' ? 'true' : nothing}
53
+ >${entry.level}</span
54
+ >
53
55
  <span part="message">${entry.text}</span>
54
56
  ${entry.count > 1
55
57
  ? html `<span part="count" aria-label="${entry.count} times"
@@ -4,6 +4,10 @@ display: block;
4
4
  inline-size: 100%;
5
5
  }
6
6
 
7
+ :host(:not([shape])) {
8
+ block-size: var(--skeleton-block-size, var(--helix-size-control));
9
+ }
10
+
7
11
  :host([shape="text"]) {
8
12
  block-size: 1em;
9
13
  }
@@ -15,13 +15,16 @@ import type { CSSResultGroup } from 'lit';
15
15
  *
16
16
  * @csspart skeleton - The placeholder shape.
17
17
  *
18
+ * @cssproperty --skeleton-block-size - The height of the block shape, which is the shape when `shape` is unset. Defaults to the control height.
18
19
  * @cssproperty --skeleton-size - The diameter of the circle shape.
19
20
  */
20
21
  export declare class HelixSkeleton extends HelixHeadlessSkeleton {
21
22
  /**
22
23
  * The shape of the placeholder: `text`, `circle`, or a block when unset.
23
24
  *
24
- * Unset is a block, which is what most missing things are.
25
+ * Unset is a block, which is what most missing things are. A block is the
26
+ * control height until you size it, with a height on the element or
27
+ * `--skeleton-block-size`.
25
28
  */
26
29
  shape?: 'text' | 'circle';
27
30
  static styles: CSSResultGroup;
@@ -59,6 +59,11 @@ outline: var(--helix-size-focus-ring) solid var(--helix-color-focus-ring);
59
59
  outline-offset: var(--helix-size-focus-offset);
60
60
  }
61
61
 
62
+ [part="column"]:focus-visible {
63
+ outline: var(--helix-size-focus-ring) solid var(--helix-color-focus-ring);
64
+ outline-offset: calc(var(--helix-size-focus-offset) * -1);
65
+ }
66
+
62
67
  [part="grip"] {
63
68
  all: unset;
64
69
  align-items: center;
@@ -8,7 +8,7 @@ display: contents;
8
8
  min-block-size: 0;
9
9
  min-inline-size: 0;
10
10
  outline: none;
11
- padding-block-start: var(--helix-space-md);
11
+ padding-block-start: var(--tabs-panel-padding-block-start, var(--helix-space-md));
12
12
  }
13
13
 
14
14
  [part="panel"][hidden] {