@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,19 +1,20 @@
1
1
  ---
2
- description: Why a component renders its own anchor, and how to keep a router.
2
+ description: Make components navigate with real anchors, and use your framework's router with them.
3
3
  section: Essentials
4
4
  title: Routing and navigation
5
5
  ---
6
6
 
7
7
  # Routing and navigation
8
8
 
9
- Some components navigate. A tree used as a sidebar, a button that goes
10
- somewhere, a tab that is really a page. Those all want to be links, and a link
11
- inside a shadow root is where web components and framework routers disagree.
9
+ Some components navigate, such as a tree used as a sidebar, a button that opens
10
+ another page, or a tab that is a page. Each of these should be a link. A
11
+ framework router does not handle a link inside a shadow root the way it handles
12
+ its own links.
12
13
 
13
14
  ## A component that navigates renders its own anchor
14
15
 
15
- `Tree.Item` and `Button` both take an `href`, and when they have one the thing
16
- you press *is* an `<a>`:
16
+ `Tree.Item` and `Button` both take an `href`. With an `href`, the element you
17
+ press *is* an `<a>`:
17
18
 
18
19
  ```html
19
20
  <helix-tree-item
@@ -24,31 +25,31 @@ you press *is* an `<a>`:
24
25
  </helix-tree-item>
25
26
  ```
26
27
 
27
- That is not a detail of the implementation. What it buys is a list of things no
28
- amount of JavaScript reproduces:
28
+ A real anchor gives you behavior that JavaScript cannot reproduce:
29
29
 
30
- - the whole row is the target, not just the words
31
- - Enter works, because a focused anchor is a thing Enter activates
32
- - middle-click and Command-click open a tab
33
- - the destination appears in the status bar on hover
34
- - right-click offers Copy Link Address
35
- - a crawler follows it
30
+ - The whole row is the target, including the space around the words.
31
+ - Enter works, because Enter activates a focused anchor.
32
+ - Middle-click and Command-click open a new tab.
33
+ - The destination appears in the status bar on hover.
34
+ - Right-click offers Copy Link Address.
35
+ - A crawler follows it.
36
36
 
37
- A `<Link>` inside each row buys none of it. In the docs sidebar the anchor was
38
- 57px of a 240px row, so five sixths of every row did nothing, and Enter did
39
- nothing at all: the tree announced a selection and no anchor heard it.
37
+ A `<Link>` inside each row gives you none of these. In our docs sidebar, the
38
+ anchor was 57px of a 240px row, so about three quarters of every row did nothing. Enter
39
+ did nothing at all, because the tree reported a selection and no anchor
40
+ received it.
40
41
 
41
42
  ## `href` and a framework's `<Link>`
42
43
 
43
44
  You cannot give a component's internal anchor to `next/link`. `<Link>` renders
44
- *its own* `<a>` and attaches its own handlers to it, and there is no way to hand
45
- that to an element that renders one inside a shadow root.
45
+ *its own* `<a>` and attaches its own handlers to it. An element that renders an
46
+ anchor inside a shadow root cannot receive those handlers.
46
47
 
47
- So it is one or the other.
48
+ Choose one: an `href` on the component, or a `<Link>`.
48
49
 
49
- ## Wrapping the component in a `<Link>` is not the third option
50
+ ## Do not wrap a component in a `<Link>`
50
51
 
51
- It looks like it should work, and it half does:
52
+ Wrapping a component in a `<Link>` only partly works:
52
53
 
53
54
  ```tsx
54
55
  {/* Don't. */}
@@ -58,23 +59,25 @@ It looks like it should work, and it half does:
58
59
  ```
59
60
 
60
61
  The tree still finds its items, because it collects them with a descendant
61
- query rather than by looking at its children. What it costs:
62
+ query. The wrapper causes three problems:
62
63
 
63
- - **Two focus stops per row.** The anchor is focusable and so is the treeitem
64
- inside it. A tree owns a single roving tab stop, and this adds a second one it
65
- does not control.
66
- - **A `treeitem` nested inside a `link`.** Anything reading the semantics meets
67
- both, in that order, for one thing.
68
- - **Nothing at all for a branch.** A branch contains its children, so wrapping
69
- one puts every descendant anchor inside an ancestor anchor. That is invalid,
70
- and browsers rearrange it.
64
+ - **Two focus stops per row.** The anchor can take focus, and so can the
65
+ treeitem inside it. A tree has a single roving tab stop, and the anchor adds a
66
+ second one that the tree does not control.
67
+ - **A `treeitem` nested inside a `link`.** Anything that reads the semantics
68
+ finds both roles, in that order, for one item.
69
+ - **Invalid markup for a branch.** A branch contains its children, so wrapping a
70
+ branch puts every descendant anchor inside an ancestor anchor. Nested anchors
71
+ are invalid, and browsers rearrange them.
71
72
 
72
73
  ## Some roles cannot contain a link
73
74
 
74
- `treeitem` and `tab` are the two in this library. You cannot nest an anchor in
75
- either, and the answer is not to give up the anchor.
75
+ In Helix, `treeitem` and `tab` are the two roles that cannot contain a link. You
76
+ cannot nest an anchor in either role. A tree keeps the anchor and changes its
77
+ markup.
76
78
 
77
- For a tree, the role moves *onto* the anchor and the group becomes its sibling:
79
+ For a tree, the role moves *onto* the anchor, and the group becomes the
80
+ anchor's sibling:
78
81
 
79
82
  ```html
80
83
  <!-- What Tree.Item renders when it has an href -->
@@ -95,20 +98,20 @@ For a tree, the role moves *onto* the anchor and the group becomes its sibling:
95
98
  </div>
96
99
  ```
97
100
 
98
- That is the navigation flavor of the tree pattern, and it is why `href` changes
99
- the shape of the markup rather than only adding an attribute. Without an `href`
100
- nothing moves: the wrapper stays the `treeitem` and holds its group, which is
101
- what an `<li>` holding a `<ul>` is.
101
+ This markup is the navigation form of the tree pattern. For that reason, `href`
102
+ changes the structure of the markup as well as adding an attribute. Without an
103
+ `href`, nothing moves. The wrapper stays the `treeitem` and contains its group,
104
+ the same way an `<li>` contains a `<ul>`.
102
105
 
103
- Tabs have no equivalent published pattern. Web Awesome hit the same wall and
104
- answered it the other way, telling consumers to drop the anchor and navigate in
105
- JavaScript. That works, and it costs every item in the list above.
106
+ Tabs have no equivalent published pattern. Web Awesome has the same problem. Its
107
+ answer is to drop the anchor and navigate in JavaScript. That works, and you
108
+ lose every item in the list above.
106
109
 
107
- ## Getting client-side routing back
110
+ ## Use client-side routing with real anchors
108
111
 
109
- Real anchors do a full page load, because a framework only intercepts anchors it
110
- rendered itself. If you want its router, put it back on top rather than giving
111
- up the anchors:
112
+ A real anchor causes a full page load, because a framework intercepts only the
113
+ anchors it rendered itself. To use the framework's router, add it on top of the
114
+ anchors:
112
115
 
113
116
  ```tsx
114
117
  'use client'
@@ -135,14 +138,14 @@ const navigate = (event) => {
135
138
  return <nav onClick={navigate} onPointerOver={warm}>{/* … */}</nav>
136
139
  ```
137
140
 
138
- One listener on the container covers every row, and nothing has to be rebound as
139
- rows come and go.
141
+ One listener on the container handles every row. You do not need to add
142
+ listeners again as rows are added and removed.
140
143
 
141
- ### Prefetch on approach, not on sight
144
+ ### Prefetch when the pointer or focus reaches a link
142
145
 
143
- `<Link>` prefetches when a link enters the viewport. For a sidebar of seventy
144
- that is seventy requests for one navigation. Prefetching on `pointerover` and
145
- `focus` instead costs one, and a reader points at a row well before they press
146
+ `<Link>` prefetches when a link enters the viewport. In a sidebar of seventy
147
+ links, that is seventy requests for one navigation. Prefetch on `pointerover`
148
+ and `focus` to make one request. People point at a row well before they press
146
149
  it:
147
150
 
148
151
  ```tsx
@@ -156,10 +159,10 @@ const warm = (event) => {
156
159
  }
157
160
  ```
158
161
 
159
- Prefetching is production-only in Next, so this does nothing in development. It
160
- is not broken.
162
+ Next prefetches only in production, so this code has no effect in development.
163
+ That is expected.
161
164
 
162
- ## Which components take an `href` today
165
+ ## Components that take an `href`
163
166
 
164
167
  | Component | Notes |
165
168
  |---|---|
@@ -167,42 +170,46 @@ is not broken.
167
170
  | `Tree.Item` | Changes the ARIA shape, as above. A linked branch navigates from its row and opens from its chevron. |
168
171
  | `Menu.Item` | The row becomes the anchor and carries `menuitem`. Takes `target`, `rel` and `download`. |
169
172
 
170
- Anything else that should navigate is a gap. Slot a link into it, and press the
171
- row rather than the words: `Tree.Item` follows a slotted anchor from anywhere in
172
- the row and from the keyboard, which covers the case without an `href`.
173
-
174
- ## `Option` cannot take one
175
-
176
- The obvious place to want a link in a list is a command palette, whose entries
177
- are usually half commands and half destinations. It is the one place this
178
- library cannot offer an `href`.
179
-
180
- **A listbox cannot move focus to its options.** A combobox keeps focus in its
181
- text field while the arrows walk the list, so the pattern it is obliged to use
182
- is `aria-activedescendant`: focus stays put and an attribute says which option
183
- is current. A menu is the opposite, and that is what makes `Menu.Item` easy:
184
- focus really moves to the row, so the row can be an anchor with `menuitem` on
185
- it.
186
-
187
- **That reference reaches the option's host and nothing inside it.** IDREFs do
188
- not cross shadow boundaries at all, so `Listbox` uses the element-reference
189
- form. Measured in Chromium: assigning `ariaActiveDescendantElement` an element
190
- inside a *sibling* shadow root reads back `null`, while assigning the option's
191
- host reads back the host. So `role="option"` is pinned to the host, since the
192
- host is the only element the listbox can point at.
193
-
194
- **And a `role="option"` with an anchor inside it is broken.** Measured in the
195
- same browser, the accessibility tree for `<div role="option"><a href>Open
196
- settings</a></div>` inside a listbox is an option with *no accessible name*, and
197
- the link exposed as a child of it. The sibling option with plain text is named
198
- correctly. So the maneuver that works for `treeitem` and `menuitem`, moving the
199
- role onto the anchor, is exactly the one thing that cannot be done here.
200
-
201
- Two constraints pointing opposite ways, and no arrangement satisfies both.
202
-
203
- **So a palette routes through its event.** Listen for `helix-select` on
204
- `helix-command` and call the router. That is what a palette is for, entries
205
- being commands and "go to Settings" being a command that navigates, and it is
206
- what every palette a reader has used already does. What it costs is the gestures
207
- only a real anchor has: middle-click, a modified click, and the destination in
208
- the status bar.
173
+ Other components that should navigate do not take an `href` yet. To make one
174
+ navigate, slot a link into it. `Tree.Item` follows a slotted anchor when you
175
+ press anywhere in the row or use the keyboard, so a tree item can navigate
176
+ without an `href`.
177
+
178
+ ## `Option` cannot take an `href`
179
+
180
+ A command palette is a common place to want links in a list, because its
181
+ entries are often half commands and half destinations. It is the one place
182
+ where Helix cannot offer an `href`.
183
+
184
+ A listbox cannot move focus to its options. A combobox keeps focus in its text
185
+ field while the arrow keys move through the list, so it has to use
186
+ `aria-activedescendant`. Focus stays in the field, and an attribute marks the
187
+ current option. A menu works the other way, which makes `Menu.Item` simple.
188
+ Focus moves to the row, so the row can be an anchor with `menuitem` on it.
189
+
190
+ The `aria-activedescendant` reference reaches the option's host and nothing
191
+ inside it. IDREFs do not cross shadow boundaries, so `Listbox` uses the
192
+ element-reference form. We measured this in Chromium. Assigning
193
+ `ariaActiveDescendantElement` an element inside a *sibling* shadow root reads
194
+ back `null`. Assigning the option's host reads back the host. So
195
+ `role="option"` stays on the host, because the host is the only element the
196
+ listbox can point at.
197
+
198
+ A `role="option"` with an anchor inside it is also broken. In the same browser,
199
+ the accessibility tree for `<div role="option"><a href>Open
200
+ settings</a></div>` inside a listbox shows an option with *no accessible name*,
201
+ with the link as its child. A sibling option with plain text has the correct
202
+ name. Moving the role onto the anchor works for `treeitem` and `menuitem`, and
203
+ it cannot work here.
204
+
205
+ No arrangement of the markup meets both of these constraints.
206
+
207
+ **In a command palette, navigate from the `helix-select` event.** Listen for
208
+ `helix-select` on `helix-command` and call your router. A palette entry is a
209
+ command, and "go to Settings" is a command that navigates. Command palettes in
210
+ other applications work the same way. You lose the gestures that only a real
211
+ anchor has:
212
+
213
+ - middle-click
214
+ - a modified click
215
+ - the destination in the status bar
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: The editor beside the preview, and how to build one of your own.
2
+ description: The editable examples on each component page, and what you need to build an editor of your own.
3
3
  section: Tooling
4
4
  title: Sandbox
5
5
  ---
@@ -8,39 +8,38 @@ title: Sandbox
8
8
 
9
9
  [Open the sandbox](/helix/sandbox).
10
10
 
11
- Every example on a component's page is editable. Change the markup and the
12
- preview redraws; change the stylesheet beside it and the preview restyles.
11
+ You can edit every example on a component's page. When you change the markup,
12
+ the preview redraws. When you change the stylesheet beside it, the preview
13
+ restyles.
13
14
 
14
- The same elements are what you would reach for to build a playground of your
15
- own, and the isolation below is what makes running someone else's markup safe.
15
+ To build a playground of your own, you would use the same elements. The
16
+ isolation described below makes it safe to run markup that someone else wrote.
16
17
 
17
- ## The frame is sandboxed
18
+ ## How the preview is isolated
18
19
 
19
- The preview runs in a sandboxed iframe with an opaque origin, and the script
20
- that drives it is served as a static classic script rather than rendered by a
21
- route.
20
+ The preview runs in a sandboxed iframe with an opaque origin. The script that
21
+ drives it is a static classic script, served as a file. No route renders it.
22
22
 
23
- **What that buys:** markup typed into the sandbox runs in the frame, and an
24
- `<img onerror>` in it cannot read the surrounding page's cookies, its
25
- `localStorage`, or the editor's contents. Before the frame was isolated, it
26
- could. Measured.
23
+ Markup you type into the sandbox runs in the frame. An `<img onerror>` in it
24
+ cannot read the cookies or `localStorage` of the page around it, or the editor's
25
+ contents. Before we isolated the frame, it could. We measured both.
27
26
 
28
- The classic script is the part that looks like packaging and is not: from an
29
- opaque origin, a module import or a `fetch` back to the site's origin is
30
- cross-origin and fails without CORS headers. A classic `<script src>` is not
31
- CORS-checked, so it is the one shape that can carry the component library into a
32
- document the page cannot reach into and which cannot reach back out.
27
+ The classic script is required for the isolation to work. From an opaque origin,
28
+ a module import or a `fetch` back to the site's origin is cross-origin, and it
29
+ fails without CORS headers. A classic `<script src>` is not CORS-checked. It is
30
+ the only way to load the component library into a document that the page cannot
31
+ reach into and that cannot reach back out.
33
32
 
34
- ## Coordination is by event
33
+ ## How the source panes and the preview communicate
35
34
 
36
- A source pane dispatches `helix-before-edit`, which bubbles, and the sandbox hands
37
- the value to whatever preview is slotted into it. Neither child knows the other
38
- exists, so either can be replaced or used alone.
35
+ A source pane dispatches `helix-before-edit`, which bubbles. The sandbox passes
36
+ the value to the preview slotted into it. The source pane and the preview do not
37
+ reference each other, so you can replace either one or use it alone.
39
38
 
40
- The event carries the pane's `name`, which is what lets one sandbox hold several
41
- panes without the root identifying senders by reference: `html` is the preview's
42
- markup and `css` is its stylesheet. A pane named anything else is showing
43
- something rather than driving it, and is ignored.
39
+ The event includes the pane's `name`, so one sandbox can hold several panes
40
+ without tracking which element sent each event. A pane named `html` sets the
41
+ preview's markup, and a pane named `css` sets its stylesheet. The sandbox
42
+ ignores a pane with any other name, so that pane only displays its contents.
44
43
 
45
44
  ```html
46
45
  <helix-sandbox>
@@ -56,21 +55,22 @@ something rather than driving it, and is ignored.
56
55
  </helix-sandbox>
57
56
  ```
58
57
 
59
- ## The console is patched in the frame
58
+ ## Console output from the example
60
59
 
61
- Logs from the example reach the page's console panel; logs from the page around
62
- it do not. That is deliberate and is the point of doing the patching in the
63
- frame rather than in the sandbox: it catches what the example itself logs and
64
- nothing else.
60
+ Logs from the example appear in the console panel. Logs from the page around it
61
+ do not. We patch the console inside the frame on purpose, so the panel shows
62
+ what the example logs and nothing else.
65
63
 
66
64
  ## The package is private
67
65
 
68
- `@fusion.dev/helix-sandbox` does not publish. It is the editor and spec runner for this
69
- repository, and it is Helix tooling rather than a Helix product: the elements
70
- assume the frame, the frame assumes the build, and the build assumes this
71
- repository's layout.
66
+ `@fusion.dev/helix-sandbox` is not published. It is the editor and spec runner
67
+ for this repository, and it is Helix tooling. It depends on the repository:
72
68
 
73
- If you want an editable example in your own documentation, the honest answer
74
- today is that you would build it, and the manifest and the `.examples` files are
75
- what you would build it from. That is a gap rather than a policy, and worth
76
- telling us about.
69
+ - The elements depend on the frame.
70
+ - The frame depends on the build.
71
+ - The build depends on this repository's layout.
72
+
73
+ To add editable examples to your own documentation today, you would build the
74
+ editor yourself, from the manifest and the `.examples` files. This is a gap we
75
+ have not filled, and we have no policy against filling it. Tell us if you need
76
+ it.