@fusion.dev/helix-docs 0.104.1 → 0.106.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 +431 -0
  19. package/content/metrics.mdx +296 -299
  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 +3548 -2682
  48. package/package.json +3 -3
package/content/react.mdx CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: The typings, the hydration bridge, and how state moves.
2
+ description: The React typings, the hydration bridge, and how state moves between React and the elements.
3
3
  parent: installation
4
4
  section: Getting started
5
5
  title: React
@@ -7,17 +7,17 @@ title: React
7
7
 
8
8
  # React
9
9
 
10
- There is no React package. The elements are custom elements and React renders
11
- them directly. What ships for React is a `jsx.d.ts` that types every tag and a
12
- runtime bridge in `@fusion.dev/helix-core` that repairs two React behaviors.
10
+ The elements are custom elements, and React renders them directly, so there is
11
+ no React package. For React, we ship a `jsx.d.ts` file that types every tag, and
12
+ a runtime bridge in `@fusion.dev/helix-core` that repairs two React behaviors.
13
13
 
14
14
  ```ts
15
15
  import '@fusion.dev/helix/jsx'
16
16
  ```
17
17
 
18
- A types-only import, once, anywhere TypeScript will see it. After that
19
- `<helix-button>` is a typed element with its real props, and a value the
20
- component does not accept is a compile error rather than a runtime shrug.
18
+ Add this types-only import once, anywhere TypeScript sees it. `<helix-button>`
19
+ then has typed props, and a value the component does not accept is a compile
20
+ error.
21
21
 
22
22
  ## Bind events with `onhelix-*`
23
23
 
@@ -25,25 +25,30 @@ component does not accept is a compile error rather than a runtime shrug.
25
25
  <helix-accordion-item onhelix-before-open={(event) => track(event.detail.open)} />
26
26
  ```
27
27
 
28
- The prop is `on` and the event name, verbatim. Not `onHelixOpen`, not `onOpen`.
29
- React 19 forwards an unknown `on*` prop to `addEventListener` using the name as
30
- written, so the casing is the event's own.
28
+ The prop is `on` followed by the exact event name. `onHelixOpen` and `onOpen` do
29
+ not work. React 19 passes an unknown `on*` prop to `addEventListener` with the
30
+ name as written, so use the event's own casing.
31
31
 
32
- ## The bridge
32
+ ## The hydration bridge
33
33
 
34
- React skips everything it would have to apply imperatively while hydrating. That
35
- is two things, and `HelixElement` repairs both.
34
+ While React hydrates, it skips everything it would have to apply imperatively.
35
+ That affects two things, and `HelixElement` repairs both.
36
36
 
37
- **Event handlers** attached during hydration are stashed on the DOM node but
38
- never bound, so an event fired before the next render finds nothing listening.
39
- The element reads React's own stash off the node and binds what it finds, so a
40
- handler works from the first paint rather than from the first re-render.
37
+ ### Event handlers
41
38
 
42
- **Object and function properties** are dropped on both sides: React's server
43
- renderer writes nothing for a prop whose value is an object, and the hydrate path
44
- never applies one. An array prop therefore arrives by no route at all, and
45
- because both halves agree, nothing warns. The element assigns them itself once it
46
- upgrades, which is what lets a data table take its rows as a plain prop:
39
+ React stores the event handlers it sees during hydration on the DOM node, but it
40
+ does not bind them. An event fired before the next render reaches no listener.
41
+ The element reads the handlers React stored on the node and binds them, so a
42
+ handler works from the first paint.
43
+
44
+ ### Object and function properties
45
+
46
+ React drops object and function properties on both sides. The server renderer
47
+ writes nothing for a prop whose value is an object, and hydration never applies
48
+ one. An array prop therefore never reaches the element, and because the server
49
+ and the client agree, React shows no warning. The element assigns these
50
+ properties itself once it upgrades, so a data table can take its rows as a plain
51
+ prop:
47
52
 
48
53
  ```tsx
49
54
  <helix-data-table label="People" rows={rows}>
@@ -53,41 +58,43 @@ upgrades, which is what lets a data table take its rows as a plain prop:
53
58
  </helix-data-table>
54
59
  ```
55
60
 
56
- Only properties the component declares, and only values an attribute could not
57
- have carried, so a string is left to the channel that already works. A `null` is
58
- left alone too, and the component's own default stands.
61
+ The element assigns only properties the component declares, and only values an
62
+ attribute cannot carry. Strings still go through attributes, which already work.
63
+ The element also leaves a `null` alone, so the component keeps its default.
64
+
65
+ The columns are elements for the same reason. `field`, `sortable`, and `width`
66
+ are strings and booleans, so they pass through server rendering intact. `cell`
67
+ and `compare` are functions, set on an element that repairs its own props.
68
+ Inside a `columns` array, the bridge could not reach them.
59
69
 
60
- The columns are elements rather than a config array for the same reason: `field`,
61
- `sortable` and `width` are strings and booleans, so they cross to the server
62
- intact, and `cell` and `compare` are functions on an element that can repair its
63
- own props. Nested inside a `columns` array they would be unreachable.
70
+ The cell values still do not render on the server. That is a separate limit.
71
+ Reading a cell needs the columns, and the server render does not pass the table
72
+ its markup children. The first paint shows a labeled table with no data in
73
+ either case. Use `helix-table` when the content must be in the served HTML.
64
74
 
65
- **The cell values still do not server-render**, which is a separate limit and not
66
- this one. Reading a cell needs the columns, and the columns are markup children
67
- the server render does not hand the table, so the first paint is a labeled shell
68
- either way. Use `helix-table` where the content has to be in the served HTML.
75
+ ### The React version
69
76
 
70
- That is runtime code you already have, in `@fusion.dev/helix-core`, and nothing to
71
- configure. One thing to know about it: it reads a React
72
- internal, the per-page key React stores props under. If a React release renames
73
- that key the bridge stops repairing and your handlers go back to needing a
74
- commit before they fire. Helix pins the React version it was measured against
75
- and fails its own build when React moves past it, so this cannot go unnoticed.
77
+ The bridge is runtime code in `@fusion.dev/helix-core`, which you already have,
78
+ and it needs no configuration. It reads a React internal: the per-page key that
79
+ React stores props under. If a React release renames that key, the bridge stops
80
+ repairing, and your handlers again need a commit before they fire. We pin the
81
+ React version we measured the bridge against, and our build fails when React
82
+ moves past it, so a rename cannot go unnoticed.
76
83
 
77
- ## A declared property is a claim of ownership
84
+ ## Seed, mirror, or refuse a value
78
85
 
79
- This is the one thing about Helix that surprises people coming from React, and
80
- it is a compile error rather than a surprise at runtime:
86
+ Helix differs from React in one way that surprises people. This markup
87
+ compiles, and the field stays editable:
81
88
 
82
89
  ```tsx
83
- // Does not compile.
90
+ // Compiles, and the field stays editable.
84
91
  <helix-input label="Name" value="Ada" />
85
92
  ```
86
93
 
87
94
  In React, `value` on a native `<input>` without `onChange` renders a read-only
88
- field and warns. **A Helix element is not read-only**: `value` is a seed and the
89
- element goes on applying its own changes, so the markup below works and needs no
90
- handler beside it.
95
+ field and logs a warning. On a Helix element, `value` is a seed. The element
96
+ starts with that value and keeps applying its own changes, so the markup below
97
+ works without a handler.
91
98
 
92
99
  ```tsx
93
100
  // Seeded: the element owns it from there.
@@ -107,17 +114,18 @@ handler beside it.
107
114
  />
108
115
  ```
109
116
 
110
- **The one habit to unlearn is refusing by not answering.** React force-restores
111
- a native `<input>` whose state did not move and does not do this for a custom
112
- element, so a handler that declines to call `setState` leaves the element
113
- holding the new value. `preventDefault()` is the refusal.
117
+ **To refuse a change, call `preventDefault()`.** Skipping `setState` does not
118
+ refuse it. React restores a native `<input>` whose state did not change, and it
119
+ does not do this for a custom element. A handler that does not call `setState`
120
+ leaves the element with the new value.
114
121
 
115
- *Until 26 Aug 2026 this page described the opposite, and the typings enforced
116
- it: writing `value` was a claim of ownership, the element applied nothing until
117
- told, and `<helix-input value="Ada" />` was a compile error without an answering
118
- handler. The constraint is deleted along with the mode.*
122
+ *Until August 26, 2026, this page described the opposite behavior, and the typings
123
+ enforced it. Writing `value` made you the owner of the state. The element
124
+ applied no change until you set the value, and `<helix-input value="Ada" />`
125
+ without a handler was a compile error. We removed that mode and its
126
+ constraint.*
119
127
 
120
- See [State and control](./state-and-control) for the whole of it.
128
+ See [State and control](./state-and-control) for the full model.
121
129
 
122
130
  ## Register in a client component
123
131
 
@@ -129,16 +137,16 @@ export const HelixElements = () => null
129
137
  ```
130
138
 
131
139
  Render it once in your layout. **Your pages stay server components.** An
132
- unregistered tag is an unknown element in the HTML that upgrades when the
133
- definition lands, so nothing about a server component needs to change.
140
+ unregistered tag is an unknown element in the HTML, and it upgrades when the
141
+ definition loads. Your server components need no changes.
134
142
 
135
143
  ## Server components get no declarative shadow DOM
136
144
 
137
- A server component's output is replayed from the flight payload through DOM APIs
138
- rather than parsed as HTML, and declarative shadow DOM cannot activate for
139
- script-inserted nodes. This is why `elements.css` exists: it styles the
140
- un-upgraded markup so the interval before hydration looks like the finished page
141
- rather than like unstyled boxes.
145
+ React inserts a server component's output by replaying the flight payload
146
+ through DOM APIs. The browser does not parse that output as HTML, and
147
+ declarative shadow DOM cannot activate for nodes inserted by script.
148
+ `elements.css` exists for this case. It styles the markup before the elements
149
+ upgrade, so the page looks finished before hydration.
142
150
 
143
- [Server rendering](./server-rendering) has the whole contract, and
144
- [Using the elements](./using-the-elements) has the rest of the binding rules.
151
+ [Server rendering](./server-rendering) covers server rendering in full, and
152
+ [Using the elements](./using-the-elements) covers the other binding rules.
@@ -1,17 +1,22 @@
1
1
  ---
2
- description: Helix styling for markup this library does not render, with no JavaScript.
2
+ description: Style your own HTML with Helix data attributes and no JavaScript.
3
3
  section: Foundations
4
4
  title: Recipes
5
5
  ---
6
6
 
7
7
  # Recipes
8
8
 
9
- `@fusion.dev/helix-styles` carries recipes for markup the components do not render: your
10
- own anchors, your own buttons, a card you built yourself, prose out of a CMS.
11
- They are `data-` attributes, they need no JavaScript, and they read the same
12
- tokens the components do.
9
+ `@fusion.dev/helix-styles` includes recipes for markup that the components do
10
+ not render, such as:
13
11
 
14
- This is what lets a page look like Helix without a single custom element on it.
12
+ - your own anchors
13
+ - your own buttons
14
+ - a card you built yourself
15
+ - prose from a CMS
16
+
17
+ Recipes are `data-` attributes. They need no JavaScript, and they use the same
18
+ tokens as the components. With recipes, your page can look like Helix without
19
+ any custom elements.
15
20
 
16
21
  ## Buttons
17
22
 
@@ -26,14 +31,18 @@ This is what lets a page look like Helix without a single custom element on it.
26
31
  >Settings</a>
27
32
  ```
28
33
 
29
- Two axes: `data-button` is how much ink it claims (`solid`, `outlined`, `ghost`,
30
- `subtle`) and `data-button-tone` is what it means (`primary`, `accent`,
31
- `neutral`, `danger`). `data-button-width` takes `auto`, `standard` or `full`.
34
+ A button recipe has two axes:
35
+
36
+ - `data-button` sets the button's emphasis: `solid`, `outlined`, `ghost`, or
37
+ `subtle`.
38
+ - `data-button-tone` sets what the button means: `primary`, `accent`,
39
+ `neutral`, or `danger`.
32
40
 
33
- **This exists for elements this library does not render**, which is the case an
34
- `<a>` styled as a button falls into. When you want a button, use `helix-button`:
35
- the recipe has no loading state, no pressed state and no `type="submit"`
36
- handling.
41
+ `data-button-width` takes `auto`, `standard`, or `full`.
42
+
43
+ **Use the button recipe only on elements that Helix does not render**, such as
44
+ an `<a>` styled as a button. For a button, use `helix-button`. The recipe has no
45
+ loading state, no pressed state, and no `type="submit"` handling.
37
46
 
38
47
  ## Links
39
48
 
@@ -50,8 +59,8 @@ handling.
50
59
  ```
51
60
 
52
61
  `data-link-size` and `data-link-underline` are the modifiers. `quiet` is for
53
- navigation, where a line under every row would be noise. A link that opens a new
54
- context says so on its own, rather than surprising the reader.
62
+ navigation, where an underline on every row would distract. If a link opens a
63
+ new tab or window, say so in the link text, so it does not surprise your users.
55
64
 
56
65
  ## Surfaces and layout
57
66
 
@@ -61,32 +70,30 @@ context says so on its own, rather than surprising the reader.
61
70
  <div data-dock="bottom-end">…</div>
62
71
  ```
63
72
 
64
- `data-dock` pins something to a corner, which is a fact about the page rather
65
- than about the control: `data-dock-within` scopes it to the nearest positioned
66
- ancestor instead of the window.
73
+ `data-dock` pins an element to a corner of the window. `data-dock-within` pins it
74
+ to a corner of the nearest positioned ancestor.
67
75
 
68
76
  ## Surface axes
69
77
 
70
- Two of the [visual axes](./visual-axes) describe a surface rather than a control,
71
- and these are how a plain element reaches them:
78
+ Two of the [visual axes](./visual-axes) describe a surface. Use these attributes
79
+ to apply them to a plain element:
72
80
 
73
81
  ```html
74
82
  <section data-material="glass">…</section>
75
83
  <div data-separation="elevated">…</div>
76
84
  ```
77
85
 
78
- `data-material` takes `glass`, `frosted` and `transparent`, and `data-separation`
79
- takes `elevated`, `filled`, `outlined` and `plain`. The values are the axes' own,
80
- so one word means the same thing here as it does on a component: a `<section>`
81
- that is `glass` and a component that is `material="glass"` are asking for the
82
- same optical treatment. See [Elevation and
83
- material](./elevation-and-material).
86
+ `data-material` takes `glass`, `frosted`, and `transparent`. `data-separation`
87
+ takes `elevated`, `filled`, `outlined`, and `plain`. The values are the same as
88
+ the axes' values, so each word means the same thing here as on a component. A
89
+ `<section>` that is `glass` and a component with `material="glass"` get the same
90
+ treatment. See [Elevation and material](./elevation-and-material).
84
91
 
85
- **Blur is the expensive one.** `glass` and `frosted` both carry a
86
- `backdrop-filter`, which makes the compositor re-read what is behind the element
87
- on every frame it moves, so a full-width band that stays on screen through a
88
- scroll pays continuously. Where the effect is about depth rather than texture,
89
- `data-separation` is the cheaper axis.
92
+ **Blur costs the most to render.** `glass` and `frosted` both use a
93
+ `backdrop-filter`. The compositor re-reads what is behind the element on every
94
+ frame in which the element moves. A full-width band that stays on screen while
95
+ you scroll pays that cost the whole time. To show depth, use `data-separation`,
96
+ which costs less.
90
97
 
91
98
  ## Prose
92
99
 
@@ -97,11 +104,16 @@ scroll pays continuously. Where the effect is about depth rather than texture,
97
104
  </article>
98
105
  ```
99
106
 
100
- Headings take the type scale, paragraphs take the measure and leading, lists and
101
- links and inline code all follow. See [Typography](./typography).
107
+ In prose:
108
+
109
+ - Headings use the type scale.
110
+ - Paragraphs use the measure and leading.
111
+ - Lists, links, and inline code follow the same styles.
102
112
 
103
- If you own the markdown renderer, map two things to components, because markup
104
- can't carry what they need:
113
+ See [Typography](./typography).
114
+
115
+ If you control the Markdown renderer, map two elements to components, because
116
+ HTML alone cannot hold what they need:
105
117
 
106
118
  - **Fenced code** to `helix-code-block`, with the fence's language as its `label`.
107
119
  A `<pre>` has no label or copy button.
@@ -109,21 +121,21 @@ can't carry what they need:
109
121
  tone. `NOTE` and `IMPORTANT` become `tone="info"`, `TIP` becomes `success`,
110
122
  `WARNING` becomes `warning`, and `CAUTION` becomes `danger`.
111
123
 
112
- Prose spaces each one the same as the element it replaces.
124
+ Prose spaces each component the same as the element it replaces.
113
125
 
114
126
  ## Region axes
115
127
 
116
- These two are API rather than styling, and they are the reason the base layer is
117
- worth importing even in an app that uses only components:
128
+ These two attributes are API. Import the base layer for them, even in an app
129
+ that uses only components:
118
130
 
119
131
  ```html
120
132
  <div data-density="sm">…</div>
121
133
  <div data-color-mode="dark">…</div>
122
134
  ```
123
135
 
124
- `data-density` sets how big the controls in a region are, and it works through
125
- custom properties so it crosses every shadow boundary on its own.
126
- `data-color-mode` sets the color mode for a subtree. See [Size and
136
+ `data-density` sets the size of the controls in a region. It works through
137
+ custom properties, so it crosses every shadow boundary. `data-color-mode` sets
138
+ the color mode for a subtree. See [Size and
127
139
  density](./size-and-density) and [Color modes](./color-modes).
128
140
 
129
141
  ## Utilities
@@ -132,11 +144,16 @@ density](./size-and-density) and [Color modes](./color-modes).
132
144
  <span data-hidden-visually>Sorting by</span>
133
145
  ```
134
146
 
135
- Clipped rather than `display: none`, so it stays in the accessibility tree. See
136
- [Accessibility](./accessibility).
147
+ `data-hidden-visually` clips the element, so it stays in the accessibility tree.
148
+ `display: none` would remove it. See [Accessibility](./accessibility).
149
+
150
+ ## When to use a component
151
+
152
+ A recipe sets appearance and has no behavior. A recipe cannot:
137
153
 
138
- ## When a recipe is the wrong tool
154
+ - manage state
155
+ - take part in a form
156
+ - trap focus
157
+ - handle the keyboard
139
158
 
140
- A recipe has appearance and no behavior. It cannot manage state, cannot
141
- participate in a form, cannot trap focus and has no keyboard handling. If you
142
- find yourself adding script to a recipe, the component is what you wanted.
159
+ If you need to add a script to a recipe, use the component.