@fusion.dev/helix-docs 0.104.0 → 0.105.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 (48) hide show
  1. package/content/accessibility.mdx +92 -71
  2. package/content/angular.mdx +26 -24
  3. package/content/browser-support.mdx +61 -58
  4. package/content/building-on-the-headless-tier.mdx +100 -74
  5. package/content/charting.mdx +98 -79
  6. package/content/coding-agents.mdx +87 -69
  7. package/content/color-modes.mdx +40 -37
  8. package/content/color.mdx +67 -54
  9. package/content/composition.mdx +85 -83
  10. package/content/elevation-and-material.mdx +32 -29
  11. package/content/events.mdx +77 -65
  12. package/content/forms.mdx +69 -58
  13. package/content/iconography.mdx +75 -71
  14. package/content/installation.mdx +77 -68
  15. package/content/intent-reference.mdx +59 -54
  16. package/content/introduction.mdx +25 -23
  17. package/content/localization.mdx +122 -82
  18. package/content/maps.mdx +222 -0
  19. package/content/metrics.mdx +294 -297
  20. package/content/motion.mdx +44 -36
  21. package/content/other-frameworks.mdx +33 -31
  22. package/content/overlays.mdx +72 -54
  23. package/content/overriding-a-component.mdx +85 -74
  24. package/content/react.mdx +72 -64
  25. package/content/recipes.mdx +64 -47
  26. package/content/routing.mdx +100 -93
  27. package/content/sandbox.mdx +39 -39
  28. package/content/server-rendering.mdx +95 -84
  29. package/content/shape-and-borders.mdx +44 -37
  30. package/content/size-and-density.mdx +51 -46
  31. package/content/space-and-size.mdx +41 -34
  32. package/content/state-and-control.mdx +63 -51
  33. package/content/surfaces-and-separation.mdx +55 -53
  34. package/content/svelte.mdx +20 -19
  35. package/content/theme-builder.mdx +42 -31
  36. package/content/theming.mdx +58 -51
  37. package/content/tiers.mdx +69 -61
  38. package/content/tokens.mdx +72 -53
  39. package/content/typography.mdx +47 -47
  40. package/content/using-helix-with-an-agent.mdx +147 -115
  41. package/content/using-the-elements.mdx +55 -50
  42. package/content/versioning.mdx +42 -35
  43. package/content/visual-axes.mdx +168 -146
  44. package/content/vue.mdx +24 -24
  45. package/content/without-a-bundler.mdx +40 -23
  46. package/dist/pages.generated.js +49 -43
  47. package/dist/sources.generated.js +3337 -2680
  48. package/package.json +3 -3
@@ -1,49 +1,62 @@
1
1
  ---
2
- description: What the library guarantees, what it cannot, and where the line is.
2
+ description: What Helix components handle for you, what you handle, and what we do not test.
3
3
  section: Foundations
4
4
  title: Accessibility
5
5
  ---
6
6
 
7
7
  # Accessibility
8
8
 
9
- Some of accessibility is structural, and a component can own it. Some of it is
10
- about your content, and no component can. Knowing which is which is the
11
- difference between a page that is accessible and a page built out of accessible
12
- parts.
9
+ Helix components handle the structural parts of accessibility, such as roles,
10
+ keys, and focus. You handle the parts that depend on your content, such as
11
+ names, heading order, and language. A page built from accessible components is
12
+ accessible only when you also handle your parts.
13
13
 
14
- ## What the library owns
14
+ ## What the components handle
15
15
 
16
- **Roles, states and keyboard interaction.** Every component implements the
17
- pattern its role implies: arrow keys move within a `Tabs`, a `Listbox` and a
18
- `Menu`, `Escape` dismisses an overlay, `Home` and `End` go to the ends, typing
19
- jumps to a match. None of that is yours to write.
16
+ ### Roles, states, and keyboard interaction
20
17
 
21
- **Focus, including trapping and restoring it.** A `Dialog` traps focus while
22
- open and returns it to whatever opened it. So do `Drawer` and the modal path of
23
- `Popover`.
18
+ Every component implements the keyboard pattern for its role. You do not write
19
+ any of it:
24
20
 
25
- **Contrast, by construction rather than by review.** The theme engine measures
26
- every foreground against the ground it is really painted on and solves for the
27
- target rather than checking afterwards. Every pairing is then re-measured on a
28
- laid-out page, including the great majority of rules that set an ink or a ground
29
- but not both, so a pairing nobody thought of is still checked. See [Surfaces and
21
+ - Arrow keys move within `Tabs`, `Listbox`, and `Menu`.
22
+ - `Escape` closes an overlay.
23
+ - `Home` and `End` go to the first and last item.
24
+ - Typing jumps to a matching item.
25
+
26
+ ### Focus
27
+
28
+ A `Dialog` traps focus while it is open and returns focus to the element that
29
+ opened it. `Drawer` and the modal mode of `Popover` do the same.
30
+
31
+ ### Contrast
32
+
33
+ The theme engine measures each foreground color against the background it is
34
+ actually drawn on. It solves for the contrast target when it computes your
35
+ colors. Every pairing is then measured again on a laid-out page. That check
36
+ includes the many rules that set only a text color or only a background, so a
37
+ pairing nobody planned is still checked. See [Surfaces and
30
38
  separation](./surfaces-and-separation).
31
39
 
32
- **Reduced motion, per component.** Fifty-nine stylesheets answer
33
- `prefers-reduced-motion` individually, because they have to: the base layer's
34
- global rule is `*, *::before, *::after` in the document, and `*` does not cross
35
- a shadow boundary, so it reaches your prose and none of the components.
36
- A component that animates without answering fails the build.
40
+ ### Reduced motion
41
+
42
+ Fifty-nine stylesheets respond to `prefers-reduced-motion` individually. Each
43
+ component needs its own rule because the base layer's global rule is
44
+ `*, *::before, *::after` in the document, and `*` does not cross a shadow
45
+ boundary. That rule reaches your prose and none of the components. The build
46
+ fails when a component animates without responding to `prefers-reduced-motion`.
47
+
48
+ ### Disabled controls
37
49
 
38
- **Disabled without disappearing.** `disabled` is mirrored to `aria-disabled`
39
- rather than removing the control from the accessibility tree, so a screen reader
40
- can still announce that the control exists and is currently off.
50
+ `disabled` is mirrored to `aria-disabled`, and the control stays in the
51
+ accessibility tree. A screen reader can still announce that the control exists
52
+ and is turned off.
41
53
 
42
- ## What you own
54
+ ## What you handle
43
55
 
44
- **Names.** A component can render a label; it cannot know what to call your
45
- thing. Every control takes `label`, and an icon-only control has nothing else to
46
- go on:
56
+ ### Names
57
+
58
+ Every control takes `label`. A component renders the label you give it, and the
59
+ words are yours to choose. An icon-only control has no other text to name it:
47
60
 
48
61
  ```html
49
62
  <helix-icon-button
@@ -52,66 +65,74 @@ go on:
52
65
  ></helix-icon-button>
53
66
  ```
54
67
 
55
- **`label` means a label and `aria-label` means a name.** They are not synonyms
56
- here. `label` renders visible text and names the control through it;
57
- `aria-label` names the control with nothing on screen. Reach for the second only
58
- when the page already makes the purpose plain, which is rarer than it feels.
68
+ ### `label` and `aria-label`
69
+
70
+ `label` renders visible text and names the control through that text.
71
+ `aria-label` names the control and shows nothing on screen. Use `aria-label`
72
+ only when the page already makes the control's purpose plain. That case is less
73
+ common than it seems.
74
+
75
+ ### Heading order
59
76
 
60
- **Heading order.** Components render the heading levels you give them, not
61
- levels they guess. A `Card` does not emit an `h2` on your behalf, because it
62
- cannot know what is above it.
77
+ Components render the heading levels you give them. A `Card` does not add an
78
+ `h2` for you, because the right level depends on the headings above the card.
63
79
 
64
- **Language and direction.** Set `lang` and `dir` on the document. Components use
65
- logical properties throughout, so a right-to-left document lays out correctly,
66
- but nothing infers the direction for you.
67
- See [Localization and RTL](./localization).
80
+ ### Language and direction
68
81
 
69
- **Live regions for your own updates.** The components announce their own state
70
- changes. A message from your application needs somewhere to be announced:
82
+ Set `lang` and `dir` on the document. Components use logical properties
83
+ throughout, so a right-to-left document lays out correctly. Helix does not
84
+ detect the direction for you. See [Localization and RTL](./localization).
85
+
86
+ ### Live regions for your own updates
87
+
88
+ Components announce their own state changes. To announce a message from your
89
+ application, put it in an `Announcement`:
71
90
 
72
91
  ```html
73
92
  <helix-announcement tone="warning">Your trial ends on Friday.</helix-announcement>
74
93
  ```
75
94
 
76
- `Announcement` is always a polite live region, never assertive. `role="alert"`
77
- interrupts whatever is being read, which is right for something that appears
78
- mid-session and wrong for something that was in the served HTML: an assertive
79
- announcement would cut across a screen-reader user on every page load, before
80
- they had read anything. Politeness costs a moment and is never wrong.
95
+ `Announcement` is always a polite live region. An assertive region, such as
96
+ `role="alert"`, interrupts whatever the screen reader is reading. That suits a
97
+ message that appears during a session. It does not suit a message in the served
98
+ HTML, because an assertive announcement would interrupt a screen-reader user on
99
+ every page load, before they had read anything. A polite announcement can arrive
100
+ a moment later, and it is always safe to use.
81
101
 
82
- Note the distinction from `Callout`, which is a passage that interrupts a
83
- document and is read where it sits. Neither is a variant of the other.
102
+ `Callout` is a different component. It is a passage that interrupts a document
103
+ and is read where it sits. Neither component is a variant of the other.
84
104
 
85
105
  ## Two things the shadow boundary changes
86
106
 
87
- **IDREFs do not cross it.** `aria-labelledby` and `aria-describedby` inside a
88
- shadow root cannot resolve to an id in your document, and the reference is
89
- dropped silently rather than erroring. This is why every control renders its own
90
- label instead of being wrapped by one, and why wiring two components together by
91
- id does not work. See [Forms and validation](./forms).
107
+ `aria-labelledby` and `aria-describedby` inside a shadow root cannot point to an
108
+ id in your document. The browser drops the reference silently and reports no
109
+ error. For this reason, every control renders its own label, and connecting two
110
+ components by id does not work. See [Forms and validation](./forms).
111
+
112
+ A `<label for>` in your markup cannot reach into a shadow root either, so it
113
+ does not name a Helix control. Use the control's own `label`.
92
114
 
93
- **`<label for>` cannot reach in either.** A `<label>` in your markup will not
94
- name a Helix control. Use the control's own `label`.
115
+ When a relationship has to cross the boundary, the components use
116
+ `ElementInternals` and element-reference properties. They are the only mechanism
117
+ that works across the boundary. Use the same tools when your own components need
118
+ such a relationship.
95
119
 
96
- Where a relationship genuinely has to cross the boundary, the components use
97
- `ElementInternals` and element-reference properties rather than ids, which is
98
- the only mechanism that works. If you need the same for your own composition,
99
- that is the tool.
120
+ ## Hide text visually
100
121
 
101
- ## Hiding things correctly
122
+ To hide text on screen and keep it for screen readers, add
123
+ `data-hidden-visually`:
102
124
 
103
125
  ```html
104
126
  <span data-hidden-visually>Sorting by</span>
105
127
  ```
106
128
 
107
- The base layer's `[data-hidden-visually]` clips rather than using
108
- `display: none` or `visibility: hidden`, both of which take the element out of
109
- the accessibility tree entirely, which is the opposite of what visually-hidden
110
- text is for.
129
+ The base layer's `[data-hidden-visually]` rule clips the element.
130
+ `display: none` and `visibility: hidden` both remove the element from the
131
+ accessibility tree, so they hide the text from screen readers too.
111
132
 
112
- ## What is not covered
133
+ ## What we do not test
113
134
 
114
- The library is tested in Chromium, by automated specs and by pixel baselines. It
115
- is not run through a screen reader on every release, and the components'
116
- patterns follow the ARIA authoring practices rather than being certified against
117
- them. Where you find a real failure, it is a bug: report it.
135
+ We test Helix in Chromium with automated specs and pixel baselines. We do not
136
+ run it through a screen reader on every release. The component patterns follow
137
+ the ARIA authoring practices, and they have not been certified against them. If
138
+ you find a real failure, report it as a bug.
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: The schema Angular needs before it will compile an unknown tag.
2
+ description: The schema Angular needs to compile Helix tags, and how to bind properties and events.
3
3
  parent: installation
4
4
  section: Getting started
5
5
  title: Angular
@@ -7,9 +7,9 @@ title: Angular
7
7
 
8
8
  # Angular
9
9
 
10
- Angular refuses to compile a template containing a tag it does not recognize.
11
- That is a good default and it is the one thing standing between you and working
12
- Helix components.
10
+ Angular does not compile a template that contains a tag it does not recognize.
11
+ To use Helix components, add `CUSTOM_ELEMENTS_SCHEMA` to the components that
12
+ render them.
13
13
 
14
14
  ```ts
15
15
  import { CUSTOM_ELEMENTS_SCHEMA, Component } from '@angular/core'
@@ -22,11 +22,12 @@ import { CUSTOM_ELEMENTS_SCHEMA, Component } from '@angular/core'
22
22
  export class SaveButton {}
23
23
  ```
24
24
 
25
- Declare it on the components that use Helix rather than globally if you can.
26
- `CUSTOM_ELEMENTS_SCHEMA` switches off unknown-element checking for the whole
27
- template it is on, so scoping it keeps the check for your own typos.
25
+ If you can, declare the schema only on the components that use Helix.
26
+ `CUSTOM_ELEMENTS_SCHEMA` turns off unknown-element checking for the whole
27
+ template it is on. A narrow scope keeps that check, which catches your own
28
+ typos, in your other components.
28
29
 
29
- ## Binding
30
+ ## Bind properties and events
30
31
 
31
32
  ```html
32
33
  <helix-input
@@ -35,26 +36,27 @@ template it is on, so scoping it keeps the check for your own typos.
35
36
  />
36
37
  ```
37
38
 
38
- `[prop]` sets a property, which is what a non-string value needs, and `(event)`
39
- binds with `addEventListener`, so the event name is its own.
39
+ `[prop]` sets a property, which a value that is not a string needs. `(event)`
40
+ binds with `addEventListener`, so use the event's own name.
40
41
 
41
- **Do not use `attr.` for a stateful property.** `[attr.value]="name"` writes
42
- the attribute, which seeds the control and is then out of date the moment a
43
- reader types; the property is the live channel.
42
+ **Do not use `attr.` for a stateful property.** `[attr.value]="name"` writes the
43
+ attribute. The attribute seeds the control and is out of date as soon as someone
44
+ types in it. The property holds the current value.
44
45
 
45
46
  ## No typings ship for Angular
46
47
 
47
- There is no `angular.d.ts` in the package. `CUSTOM_ELEMENTS_SCHEMA` turns off
48
- the checking that typings would feed, so a generated file would describe types
49
- Angular has already agreed not to enforce. The manifest is published as
50
- `@fusion.dev/helix/custom-elements.json` if you want to generate something for your
51
- own codebase from it.
48
+ The package has no `angular.d.ts`. `CUSTOM_ELEMENTS_SCHEMA` turns off the
49
+ checking that typings would feed, so Angular would not enforce a generated file.
50
+ We publish the manifest as `@fusion.dev/helix/custom-elements.json`, so you can
51
+ generate your own files from it.
52
52
 
53
- The practical consequence is that a misspelled property fails silently here
54
- where it is a compile error in React, Vue and Svelte. Worth a note in review.
53
+ As a result, a misspelled property fails without an error in Angular. In React,
54
+ Vue, and Svelte, the same mistake is a compile error. Check property names in
55
+ code review.
55
56
 
56
- ## Taking a decision
57
+ ## Refuse a change
57
58
 
58
- `value`, `open`, `checked` and `active` are seeds: the element applies its own
59
- changes from there. To take one decision, listen for the `helix-before-*` event
60
- and call `preventDefault()`. See [State and control](./state-and-control).
59
+ `value`, `open`, `checked`, and `active` are seeds. The element starts with the
60
+ value you set and then applies its own changes. To refuse a change, listen for
61
+ the `helix-before-*` event and call `preventDefault()`. See
62
+ [State and control](./state-and-control).
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: The platform features this is built on, and what is actually tested.
2
+ description: The browser features Helix uses, and the browser we test in.
3
3
  order: 5
4
4
  section: Getting started
5
5
  title: Browser support
@@ -7,21 +7,20 @@ title: Browser support
7
7
 
8
8
  # Browser support
9
9
 
10
- Helix is built on the platform rather than on a compatibility layer. There is no
11
- polyfill story, no transpiled fallback, and no configuration that trades
12
- features for reach: what the library needs is what your browser has to have.
10
+ Helix uses browser features directly, with no compatibility layer. It has no
11
+ polyfills, no transpiled fallback, and no setting that trades features for
12
+ support in older browsers. Your browser needs every feature Helix uses.
13
13
 
14
- ## What is tested
14
+ ## What we test
15
15
 
16
- **Chromium, through Playwright.** The DOM specs run in it, and 83 pixel
16
+ We test in Chromium, through Playwright. The DOM specs run in it, and 83 pixel
17
17
  baselines are captured in it, on Linux, with the network blocked.
18
18
 
19
- That is the honest extent of it. Everything below is a statement about which
20
- platform features the library uses, not a claim that we run the suite in Firefox
21
- and Safari. If you ship Helix to those and find something broken, that is a bug
22
- report we want, and it is not one we would have caught.
19
+ We do not run the tests in Firefox or Safari. The rest of this page lists the
20
+ browser features Helix uses. If you ship Helix to Firefox or Safari and find
21
+ something broken, please report it. Our tests would not have caught it.
23
22
 
24
- ## What the library uses
23
+ ## Browser features Helix uses
25
24
 
26
25
  | Feature | Where |
27
26
  |---|---|
@@ -35,55 +34,59 @@ report we want, and it is not one we would have caught.
35
34
  | `:state()` | Every boolean state the library publishes: `open`, `checked`, `selected`, `invalid`, `read`, `has-label`, `settled`, `empty`, `controls` |
36
35
  | `CSS.highlights` | Filter-match highlighting, in `Command` and `Combobox` |
37
36
 
38
- Positioning is `@floating-ui/dom` rather than CSS anchor positioning, which is a
39
- deliberate choice and the one place the library takes a dependency instead of
40
- the platform: anchor positioning is not yet available everywhere the rest of
41
- this list is.
42
-
43
- ## What decides your floor
44
-
45
- **`light-dark()` and the Popover API.** Everything else on that list has been
46
- available longer. If you need to know whether a browser can run Helix, those are
47
- the two to check, because they are the newest things the library assumes and
48
- they are load-bearing: the first is how every token expresses both color modes
49
- in one declaration, and the second is how every overlay is drawn in the top
50
- layer.
51
-
52
- Chromium 132 has both and still misses two things a page's stylesheet can say
53
- through `::part()`, measured 15 Sep 2026: it ignores a range input's
54
- `::-webkit-slider-thumb` and `::-webkit-slider-runnable-track` after a part, and
55
- it looks for the `@keyframes` a `::part()` rule names inside the component's
56
- shadow root, so no animation starts; the headless examples use neither.
57
-
58
- `:state()` is the third and is deliberately not with them, because it does
59
- degrade: a component that cannot record a state keeps whatever its resting rule
60
- draws, so a checkbox is drawn unchecked rather than drawn wrong or not at all.
61
- It is feature-tested at the one place that records any of them.
62
-
63
- Neither degrades usefully. A browser without `light-dark()` gets no color at
64
- all rather than one mode; a browser without popover gets overlays that render in
65
- flow. This is why there is no support table with a fallback column: there is no
66
- partial mode to be in.
67
-
68
- ## `CSS.highlights` is the one exception
69
-
70
- `Command` and `Combobox` highlight the matched substring in their filtered
71
- results using the CSS Custom Highlight API. It is the least widely available
72
- thing here, and it is the only one that degrades gracefully: without it the
73
- results still filter, sort and select correctly, and the match is simply not
37
+ Positioning uses `@floating-ui/dom`. We chose this on purpose, and it is the one
38
+ place Helix takes a dependency for something the browser could do. CSS anchor
39
+ positioning is not yet available in every browser that has the rest of these
40
+ features.
41
+
42
+ ## Which features set the minimum browser
43
+
44
+ **Check for `light-dark()` and the Popover API.** They are the newest features
45
+ Helix requires, and everything else in the table has been available longer.
46
+ Helix depends on both. Every token uses `light-dark()` to set both color modes
47
+ in one declaration. Every overlay uses the Popover API to draw in the top layer.
48
+
49
+ Neither one has a useful fallback:
50
+
51
+ - A browser without `light-dark()` shows no color at all.
52
+ - A browser without the Popover API renders overlays in the page flow.
53
+
54
+ For that reason, this page has no support table with a fallback column. Helix
55
+ has no partial mode.
56
+
57
+ Chromium 132 has both features. We measured on September 15, 2026 that it
58
+ does not support two things your stylesheet can do through `::part()`:
59
+
60
+ - It ignores a range input's `::-webkit-slider-thumb` and
61
+ `::-webkit-slider-runnable-track` after a part.
62
+ - It looks for the `@keyframes` that a `::part()` rule names inside the
63
+ component's shadow root, so no animation starts.
64
+
65
+ The headless examples use neither.
66
+
67
+ `:state()` is the third newest feature. It does not set the minimum browser,
68
+ because components still work without it. A component that cannot record a
69
+ state keeps the look of its resting rule, so a checkbox is drawn unchecked.
70
+ Helix tests for `:state()` support in the one place that records states.
71
+
72
+ ## Browsers without `CSS.highlights`
73
+
74
+ `Command` and `Combobox` use the CSS Custom Highlight API to highlight the
75
+ matched text in their filtered results. It is the least widely available
76
+ feature Helix uses, and it is the only one that degrades gracefully. Without it,
77
+ the results still filter, sort, and select correctly, and the match is not
74
78
  highlighted.
75
79
 
76
- Nothing else in the library reads it.
80
+ Nothing else in Helix uses it.
77
81
 
78
- ## No IE, no polyfills, no build flag
82
+ ## Older browsers
79
83
 
80
- There is no `browserslist` and no transpilation target, which is a decision
81
- rather than an oversight. A design system whose components are
82
- custom elements cannot be shipped to a browser without custom elements, and
83
- everything else on the list above arrived in the same generation of browsers
84
- that shipped those.
84
+ Helix does not support Internet Explorer and uses no polyfills. It has no
85
+ `browserslist`, no transpilation target, and no build flag for older browsers.
86
+ We decided this on purpose. Components that are custom elements cannot run in a
87
+ browser without custom elements, and every other feature in the table arrived in
88
+ the same generation of browsers.
85
89
 
86
- If you need to support something older, the honest answer is the base layer:
87
- `@fusion.dev/helix-styles` is plain CSS with no JavaScript, and a page built from its
88
- recipes works considerably further back than the components do.
89
- See [Without a bundler](./without-a-bundler).
90
+ To support an older browser, use the base layer. `@fusion.dev/helix-styles` is
91
+ plain CSS with no JavaScript. Markup built from its recipes works in much older
92
+ browsers than the components do. See [Without a bundler](./without-a-bundler).