@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/forms.mdx CHANGED
@@ -1,14 +1,20 @@
1
1
  ---
2
- description: Real form participation, labels that resolve across a shadow root, and the disabled trap.
2
+ description: How Helix controls take part in forms, render their own labels, and handle disabled fieldsets.
3
3
  section: Essentials
4
4
  title: Forms and validation
5
5
  ---
6
6
 
7
7
  # Forms and validation
8
8
 
9
- Twenty-two families are form-associated custom elements, so a `<form>` collects
10
- them, resets them, restores them on back-navigation and validates them the way it
11
- does a native control. Nothing about that is simulated.
9
+ Twenty-two component families are form-associated custom elements. A `<form>`
10
+ treats them as it treats native controls:
11
+
12
+ - It collects their values.
13
+ - It resets them.
14
+ - It restores them when you navigate back.
15
+ - It validates them.
16
+
17
+ None of this is simulated.
12
18
 
13
19
  ```html
14
20
  <form>
@@ -28,10 +34,10 @@ does a native control. Nothing about that is simulated.
28
34
  </form>
29
35
  ```
30
36
 
31
- `new FormData(form)` has `name` and `plan` in it. So does a server reading the
32
- POST body.
37
+ `new FormData(form)` contains `name` and `plan`, and so does the POST body a
38
+ server receives.
33
39
 
34
- ## Every control carries its own label
40
+ ## Every control renders its own label
35
41
 
36
42
  ```html
37
43
  <helix-input
@@ -42,27 +48,30 @@ POST body.
42
48
  ></helix-input>
43
49
  ```
44
50
 
45
- There is no wrapping `Field` component, and that is not a style preference. **A
46
- wrapper cannot name what it wraps.** Its label would live in one shadow root and
47
- the control in another, and measured in Chrome 148: an `aria-labelledby` IDREF
48
- cannot see across a shadow boundary, and `control.ariaLabelledByElements =
49
- [labelInTheWrapper]` reads back as zero elements, the reference silently
50
- dropped. The same assignment with a label in the control's own tree is kept.
51
+ Helix has no wrapping `Field` component, because **a wrapper cannot name the
52
+ control inside it.** The wrapper's label would be in one shadow root and the
53
+ control in another. We measured this in Chrome 148:
54
+
55
+ - An `aria-labelledby` IDREF cannot see across a shadow boundary.
56
+ - `control.ariaLabelledByElements = [labelInTheWrapper]` reads back as zero
57
+ elements, because the reference is silently dropped.
58
+ - The same assignment with a label in the control's own tree is kept.
51
59
 
52
- That pattern works in React component libraries because they have no shadow
53
- boundary. Bringing it here brings the ergonomics and none of the conditions that
54
- made it work.
60
+ The wrapper pattern works in React component libraries because they have no
61
+ shadow boundary. Copying it into Helix would copy the API without the condition
62
+ that makes it work.
55
63
 
56
- So every control that wants a label has the same three properties and renders
57
- its own nodes:
64
+ Every control that takes a label has the same three properties, and it renders
65
+ their content itself:
58
66
 
59
67
  | Property | For |
60
68
  |---|---|
61
69
  | `label` | The accessible name, and the fallback for the `label` slot |
62
70
  | `hint` | Help under the control, while there is nothing wrong |
63
- | `feedback` | What is wrong, in your words rather than the browser's |
71
+ | `feedback` | What is wrong, in your own words. It replaces the browser's message |
64
72
 
65
- Each is a slot fallback, so anything richer than a string replaces it:
73
+ Each property is the fallback for a slot of the same name. To use more than
74
+ plain text, put content in the slot:
66
75
 
67
76
  ```html
68
77
  <helix-input name="email">
@@ -70,16 +79,18 @@ Each is a slot fallback, so anything richer than a string replaces it:
70
79
  </helix-input>
71
80
  ```
72
81
 
73
- **A whitespace-only text node defeats a default slot's fallback**, which costs a
74
- label and gives no warning. Measured in Chrome 148: a newline between the tags
75
- is enough, so `<helix-checkbox label="Accept">` closed on the next line renders
76
- no label at all. Named slots do not have this problem, because `slot="label"` is
77
- an attribute and a text node has none. It is the platform's behavior rather
78
- than ours, and it is true of every slot fallback in the library.
82
+ A text node that holds only whitespace replaces a default slot's fallback. You
83
+ lose the label, and nothing warns you. We measured this in Chrome 148. A newline
84
+ between the tags is enough, so `<helix-checkbox label="Accept">` with its
85
+ closing tag on the next line renders no label at all. Named slots do not have
86
+ this problem, because `slot="label"` is an attribute and a text node cannot
87
+ carry one. This is how the platform behaves, and it applies to every slot
88
+ fallback in Helix.
79
89
 
80
90
  ## Validation
81
91
 
82
- The platform's own validation works, because these are real form participants:
92
+ The platform's own validation works, because these controls are real form
93
+ participants:
83
94
 
84
95
  ```html
85
96
  <helix-input
@@ -90,12 +101,13 @@ The platform's own validation works, because these are real form participants:
90
101
  ></helix-input>
91
102
  ```
92
103
 
93
- Submitting an empty form blocks and points at the control. `checkValidity()`,
94
- `reportValidity()` and `:invalid` behave.
104
+ Submitting an empty form is blocked, and the browser points to the control.
105
+ `checkValidity()`, `reportValidity()`, and `:invalid` work as they do for native
106
+ controls.
95
107
 
96
- **`feedback` overrides the message.** Left empty, the browser's own is shown,
97
- which is localized and familiar; set, it says what you want in the words your
98
- product uses:
108
+ `feedback` replaces the browser's message. When `feedback` is empty, the
109
+ browser shows its own message, which is localized and familiar. When you set it,
110
+ the control shows the message in the words your product uses:
99
111
 
100
112
  ```html
101
113
  <helix-input
@@ -106,13 +118,13 @@ product uses:
106
118
  ></helix-input>
107
119
  ```
108
120
 
109
- **A group is the participant, not its members.** `Radio.Group` and
110
- `Toggle.Group` own the `name`, the `value` and the validity: a lone radio is not
111
- a control at all, it is an answer to one. `Checkbox.Group` is the exception in
112
- the other direction, since each box in it submits itself already, so the group
113
- deliberately reports no value of its own.
121
+ `Radio.Group` and `Toggle.Group` take part in the form for their members. Each
122
+ group owns the `name`, the `value`, and the validity. A single radio is an
123
+ answer, and the group is the control it answers. `Checkbox.Group` works the
124
+ other way. Each checkbox in it already submits itself, so the group reports no
125
+ value of its own.
114
126
 
115
- ## The disabled trap
127
+ ## Disabled controls and fieldsets
116
128
 
117
129
  ```html
118
130
  <fieldset disabled>
@@ -123,13 +135,13 @@ deliberately reports no value of its own.
123
135
  </fieldset>
124
136
  ```
125
137
 
126
- That works. The platform tells a form-associated element it has been disabled by
127
- an ancestor through `formDisabledCallback`, and every Helix control implements
128
- it, so a control inside a disabled fieldset refuses input and announces itself
129
- as off rather than staying typeable and unsubmittable.
138
+ A Helix control inside a disabled fieldset is disabled too. The platform calls
139
+ `formDisabledCallback` to tell a form-associated element that an ancestor
140
+ disabled it, and every Helix control implements that callback. A control inside
141
+ a disabled fieldset does not accept input, and it announces itself as turned
142
+ off.
130
143
 
131
- **For your own CSS this means one rule.** Style disabled controls with
132
- `:disabled`, never `[disabled]`:
144
+ **Style disabled controls with `:disabled`.** Do not use `[disabled]`:
133
145
 
134
146
  ```css
135
147
  /* Right: matches the attribute and the ancestor fieldset. */
@@ -139,23 +151,22 @@ helix-input:disabled { … }
139
151
  helix-input[disabled] { … }
140
152
  ```
141
153
 
142
- The pseudo-class is true for a control carrying the attribute *and* for one
143
- inside a disabled fieldset, which sets no attribute on anything. A component
144
- styled with the attribute selector refuses interaction from the fieldset,
145
- because the platform disables it either way, and paints itself as live. That
146
- reads as a control that is simply broken, and it is invisible in every example
147
- that does not happen to use a fieldset. The library holds itself to this, and
148
- the same reasoning applies to yours.
154
+ `:disabled` matches a control that has the attribute. It also matches a control
155
+ inside a disabled fieldset, which sets no attribute on anything. With the
156
+ attribute selector, the platform still disables a control inside a disabled
157
+ fieldset, but the control is drawn as if it were active. It looks broken, and
158
+ you will not see the problem in any example without a fieldset. Helix components
159
+ follow this rule, and the same reasoning applies to your styles.
149
160
 
150
- `disabled` is mirrored to `aria-disabled` rather than removing the control from
151
- the accessibility tree, so a screen reader can still announce that the control
152
- exists and is currently off.
161
+ `disabled` is mirrored to `aria-disabled`, and the control stays in the
162
+ accessibility tree. A screen reader can still announce that the control exists
163
+ and is turned off.
153
164
 
154
- ## Seeding a form
165
+ ## Set starting values in a form
155
166
 
156
- A declared `value` or `checked` is a seed and the control goes on managing
157
- itself, so a form needs nothing wired to work. `default-value` is what a reset
158
- returns to, which is usually what a seed in a form means:
167
+ A `value` or `checked` you set is a starting value, and the control then manages
168
+ itself, so a form works without any wiring. `default-value` is the value a reset
169
+ returns to. In a form, that is usually what you mean by a starting value:
159
170
 
160
171
  ```html
161
172
  <helix-input
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: 105 glyphs, one geometry, and how to draw one that is not in the set.
2
+ description: Draw icons from the Helix set, import glyphs directly, and build a set of your own.
3
3
  section: Foundations
4
4
  title: Iconography
5
5
  ---
@@ -10,14 +10,14 @@ title: Iconography
10
10
  <helix-icon name="trash"></helix-icon>
11
11
  ```
12
12
 
13
- 108 glyphs, from Phosphor, drawn on a 256 box at Phosphor's bold weight. That is
14
- Phosphor's own geometry and it is kept rather than rescaled: normalizing to a 24
15
- box would change the optical weight of every glyph and stop the set matching
16
- anything upstream. Bold rather than regular, because Phosphor's regular is a
17
- hairline beside the medium weight this library sets its text in, and an icon
18
- that reads lighter than the words next to it is the opposite of a mark.
13
+ Helix has 125 glyphs, 120 of them from Phosphor, drawn on a 256 box at
14
+ Phosphor's bold weight. We keep Phosphor's own geometry. Rescaling to a 24 box
15
+ would change the optical weight of every glyph, and the set would stop matching
16
+ Phosphor. We use the bold weight because Phosphor's regular weight is a
17
+ hairline beside the medium weight of Helix text. An icon that looks lighter than
18
+ the words next to it does not work as a mark.
19
19
 
20
- ## Size and color come from the context
20
+ ## Size and color come from the surrounding text
21
21
 
22
22
  An icon has no `size` and no `color` property. It is `1em` square and
23
23
  `currentColor`, so it takes the size and color of the text it sits in:
@@ -28,11 +28,10 @@ An icon has no `size` and no `color` property. It is `1em` square and
28
28
  </span>
29
29
  ```
30
30
 
31
- Inside a control, `--helix-size-icon` is what decides, and it follows the
32
- region's density along with everything else. That is why an icon in a small
33
- button is smaller without anybody saying so.
31
+ Inside a control, `--helix-size-icon` sets the size, and it follows the region's
32
+ density. So an icon in a small button is smaller without any extra setting.
34
33
 
35
- ## Decorative or named
34
+ ## Decorative and named icons
36
35
 
37
36
  ```html
38
37
  <!-- Decorative: the text beside it already says what this is. -->
@@ -48,11 +47,11 @@ button is smaller without anybody saying so.
48
47
  ></helix-icon-button>
49
48
  ```
50
49
 
51
- An icon with no `label` is hidden from assistive technology, which is right when
52
- the words are beside it and wrong when they are not. `Icon.Button` takes a
53
- `label` because it never has words beside it.
50
+ An icon with no `label` is hidden from assistive technology. Leave out `label`
51
+ when text beside the icon says what it is. Add a `label` when there is no text.
52
+ `Icon.Button` takes a `label` because it never has text beside it.
54
53
 
55
- ## One that points along the line
54
+ ## Mirror an icon on right-to-left pages
56
55
 
57
56
  ```html
58
57
  <helix-icon
@@ -61,23 +60,23 @@ the words are beside it and wrong when they are not. `Icon.Button` takes a
61
60
  ></helix-icon>
62
61
  ```
63
62
 
64
- The names are physical, so `arrow-right` points right on every page. An arrow
65
- that means forward or back asks with `mirror` to turn round on a right-to-left
66
- page, which every caret the library draws already does.
63
+ Icon names describe physical directions, so `arrow-right` points right on every
64
+ page. When an arrow means forward or back, add `mirror` to flip it on a
65
+ right-to-left page. Every caret Helix draws already flips.
67
66
  [Localization](./localization) has the rest.
68
67
 
69
- ## Importing a glyph directly
68
+ ## Import a glyph
70
69
 
71
70
  ```ts
72
71
  import { trash } from '@fusion.dev/helix-icons'
73
72
  ```
74
73
 
75
- Each glyph is data, `{ content, name, viewBox }`, so you can put one in your own
76
- markup, in a canvas, or anywhere an element is not what you want. Every `name`
77
- the library uses is held to the set of glyphs that actually exist, so a typo is a
78
- build failure rather than an empty box.
74
+ Each glyph is data in the form `{ content, name, viewBox }`. You can put one in
75
+ your own markup, in a canvas, or anywhere you do not want an element. Every
76
+ `name` Helix uses is checked against the glyphs that exist, so a typo fails the
77
+ build.
79
78
 
80
- **Hand one to the element and it draws it**, whatever set it came from:
79
+ **Pass a glyph to the `icon` property to draw it**, from any set:
81
80
 
82
81
  ```tsx
83
82
  import { trash } from '@fusion.dev/helix-icons'
@@ -85,44 +84,49 @@ import { trash } from '@fusion.dev/helix-icons'
85
84
  <helix-icon icon={trash} />
86
85
  ```
87
86
 
88
- A property rather than an attribute, because an object is not a string: the
89
- frameworks set properties and plain HTML cannot, which is what `name` is for.
90
- Both set, the value wins.
87
+ `icon` is a property because a glyph is an object, and an attribute holds only a
88
+ string. Frameworks can set properties. Plain HTML cannot, so it uses `name`. If
89
+ both are set, `icon` wins.
91
90
 
92
- **This is the door that scales.** `name` resolves through an index of the whole
93
- set, which no bundler can shake, so a consumer who renders one named icon ships
94
- all of it: 15 kB gzipped for 108 glyphs. A value ships the one glyph you
95
- imported. It is also the door out of this set entirely, since anything exporting
96
- that shape works, whether it is Phosphor at another weight, another library, or
97
- your own drawings:
91
+ `icon` also keeps your bundle small. `name` looks up glyphs in an index of the
92
+ whole set, and no bundler can tree-shake that index. If you render one icon by
93
+ `name`, you ship the whole set: 17 kB gzipped for 125 glyphs. With `icon`, you
94
+ ship only the glyph you import.
95
+
96
+ `icon` also draws glyphs from outside this set. Anything that exports the same
97
+ shape works:
98
+
99
+ - Phosphor at another weight
100
+ - another icon library
101
+ - your own drawings
98
102
 
99
103
  ```ts
100
104
  const dot = { content: '<circle cx="12" cy="12" r="10" fill="currentColor" />', name: 'dot', viewBox: '0 0 24 24' }
101
105
  ```
102
106
 
103
- ## Weight is a subpath
107
+ ## Choose a weight with a subpath
104
108
 
105
109
  ```ts
106
110
  import { caretDown } from '@fusion.dev/helix-icons/regular'
107
111
  import { trash } from '@fusion.dev/helix-icons/bold'
108
112
  ```
109
113
 
110
- The default is bold, and `/bold` is the same set under a name that says so.
114
+ The default is bold. `/bold` is the same set, with the weight in its path.
111
115
 
112
- **A subpath rather than an attribute, because a weight is data rather than a
113
- dial.** Phosphor's weights are outlined filled paths: `caret-down` is `a12,12`
114
- at bold and `a8,8` at regular, so there is nothing for CSS to turn and every
115
- weight offered is bytes shipped. A subpath is shaken per import site; a `weight`
116
- attribute would put every weight into the index that `name` reads, taking one
117
- consumer's 15 kB to about 90.
116
+ **Each weight is a separate set of drawings, so each weight is a subpath.**
117
+ Phosphor's weights are outlined, filled paths. `caret-down` is `a12,12` at bold
118
+ and `a8,8` at regular, so CSS cannot turn one weight into another, and every
119
+ weight offered adds bytes. Bundlers tree-shake a subpath at each import site. A
120
+ `weight` attribute would put every weight into the index that `name` reads, and
121
+ raise your bundle from 15 kB to about 90.
118
122
 
119
- Only the default weight carries that index, so `name` draws bold. A consumer who
120
- wants the set in another weight imports the values and hands them over.
123
+ Only the default weight has that index, so `name` always draws bold. To use the
124
+ set in another weight, import the glyphs and pass them to `icon`.
121
125
 
122
- ## A set of your own
126
+ ## Build your own icon set
123
127
 
124
- The generator that builds this set is the package's own command, so you are not
125
- waiting on us to carry a glyph:
128
+ The package includes the command that builds this set, so you can add any glyph
129
+ yourself:
126
130
 
127
131
  ```sh
128
132
  npx helix-icons build --source ./brand-icons --out src/icons.ts
@@ -134,9 +138,9 @@ import { peak } from './icons'
134
138
  <helix-icon icon={peak} />
135
139
  ```
136
140
 
137
- It reads a directory of SVG files and writes the module the element draws from,
138
- which is the same job whether the drawings are yours, another library's, or the
139
- 1,404 Phosphor glyphs this set does not carry:
141
+ The command reads a directory of SVG files and writes the module that
142
+ `helix-icon` draws from. It works the same way for your own drawings, another
143
+ library's, or the 1,404 Phosphor glyphs this set does not include:
140
144
 
141
145
  ```sh
142
146
  npx helix-icons build \
@@ -144,30 +148,30 @@ npx helix-icons build \
144
148
  --list mine.txt --out src/icons.ts
145
149
  ```
146
150
 
147
- `--suffix` is for a set that spells the weight into the file name, Phosphor's
148
- bold being `caret-down-bold.svg`. `--index` also writes the record `name` reads,
149
- which you want if your own elements take a name in markup and do not want
150
- otherwise, since it is the part nothing can drop.
151
+ Use `--suffix` for a set that puts the weight in the file name, such as
152
+ Phosphor's bold `caret-down-bold.svg`. `--index` also writes the record that
153
+ `name` reads. Use it only if your own elements take a name in markup, because no
154
+ bundler can remove that record.
151
155
 
152
- **A name in two sources is refused rather than decided.** A precedence decides
153
- silently, and the glyph on the page would be whichever directory sat higher with
154
- nothing looking wrong afterwards.
156
+ **The command stops with an error when a name appears in two sources.** With
157
+ an order of precedence, the glyph on the page would come from whichever
158
+ directory ranked higher, and nothing would look wrong.
155
159
 
156
- ## Adding one to this set
160
+ ## Add a glyph to the Helix set
157
161
 
158
- Add the name to `helix/icons/icons.txt` and run `turbo run icons`. Phosphor names
159
- resolve from `@phosphor-icons/core` at build time, so adding a glyph is adding a
160
- line rather than hand-transcribing a path into a TypeScript object.
162
+ Add the name to `helix/icons/icons.txt` and run `turbo run icons`. Phosphor
163
+ names resolve from `@phosphor-icons/core` at build time, so adding a glyph takes
164
+ one line. You do not copy a path into a TypeScript object by hand.
161
165
 
162
- A mark Phosphor does not draw goes into `helix/icons/raw` as an SVG under the
163
- same name, and is read from there when the package has no match. Keep it to the
164
- same geometry, a 256 box at the bold weight: a glyph drawn at a different weight
165
- is visible immediately when it sits beside one that was not.
166
+ For a mark Phosphor does not draw, add an SVG with the same name to
167
+ `helix/icons/raw`. The build reads it from there when Phosphor has no match.
168
+ Draw it with the same geometry, a 256 box at the bold weight. A glyph drawn at a
169
+ different weight stands out next to the others.
166
170
 
167
- ## Icons this set does not have
171
+ ## Product marks
168
172
 
169
- Product marks. Helix and Cortex have marks and they live in the icon package,
170
- but a mark is not an icon: it identifies a product and does not mean an action.
171
- **A mark is one rounded body with material removed**, never a pictogram of a
172
- metaphor, because the parent it stands beside is a geometric monogram. If you
173
- are reaching for the icon set to draw a logo, that is the wrong drawer.
173
+ Helix and Cortex have product marks. They are in the icon package, and they are
174
+ not part of the icon set. A mark identifies a product. An icon stands for an
175
+ action. **A mark is one rounded body with material removed.** It is never a
176
+ pictogram of a metaphor, because it stands beside the parent brand's mark, a
177
+ geometric monogram. Do not use the icon set to draw a logo.