@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.
- package/content/accessibility.mdx +92 -71
- package/content/angular.mdx +26 -24
- package/content/browser-support.mdx +61 -58
- package/content/building-on-the-headless-tier.mdx +100 -74
- package/content/charting.mdx +98 -79
- package/content/coding-agents.mdx +87 -69
- package/content/color-modes.mdx +40 -37
- package/content/color.mdx +67 -54
- package/content/composition.mdx +85 -83
- package/content/elevation-and-material.mdx +32 -29
- package/content/events.mdx +77 -65
- package/content/forms.mdx +69 -58
- package/content/iconography.mdx +75 -71
- package/content/installation.mdx +77 -68
- package/content/intent-reference.mdx +59 -54
- package/content/introduction.mdx +25 -23
- package/content/localization.mdx +122 -82
- package/content/maps.mdx +222 -0
- package/content/metrics.mdx +294 -297
- package/content/motion.mdx +44 -36
- package/content/other-frameworks.mdx +33 -31
- package/content/overlays.mdx +72 -54
- package/content/overriding-a-component.mdx +85 -74
- package/content/react.mdx +72 -64
- package/content/recipes.mdx +64 -47
- package/content/routing.mdx +100 -93
- package/content/sandbox.mdx +39 -39
- package/content/server-rendering.mdx +95 -84
- package/content/shape-and-borders.mdx +44 -37
- package/content/size-and-density.mdx +51 -46
- package/content/space-and-size.mdx +41 -34
- package/content/state-and-control.mdx +63 -51
- package/content/surfaces-and-separation.mdx +55 -53
- package/content/svelte.mdx +20 -19
- package/content/theme-builder.mdx +42 -31
- package/content/theming.mdx +58 -51
- package/content/tiers.mdx +69 -61
- package/content/tokens.mdx +72 -53
- package/content/typography.mdx +47 -47
- package/content/using-helix-with-an-agent.mdx +147 -115
- package/content/using-the-elements.mdx +55 -50
- package/content/versioning.mdx +42 -35
- package/content/visual-axes.mdx +168 -146
- package/content/vue.mdx +24 -24
- package/content/without-a-bundler.mdx +40 -23
- package/dist/pages.generated.js +49 -43
- package/dist/sources.generated.js +3337 -2680
- package/package.json +3 -3
package/content/forms.mdx
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
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
|
|
10
|
-
|
|
11
|
-
|
|
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)`
|
|
32
|
-
|
|
37
|
+
`new FormData(form)` contains `name` and `plan`, and so does the POST body a
|
|
38
|
+
server receives.
|
|
33
39
|
|
|
34
|
-
## Every control
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
53
|
-
boundary.
|
|
54
|
-
|
|
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
|
-
|
|
57
|
-
|
|
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
|
|
71
|
+
| `feedback` | What is wrong, in your own words. It replaces the browser's message |
|
|
64
72
|
|
|
65
|
-
Each is a slot
|
|
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
|
-
|
|
74
|
-
label and
|
|
75
|
-
is enough, so `<helix-checkbox label="Accept">`
|
|
76
|
-
no label at all. Named slots do not have
|
|
77
|
-
an attribute and a text node
|
|
78
|
-
|
|
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
|
|
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
|
|
94
|
-
`reportValidity()
|
|
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
|
-
|
|
97
|
-
which is localized and familiar
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
it,
|
|
129
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
143
|
-
inside a disabled fieldset, which sets no attribute on anything.
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
##
|
|
165
|
+
## Set starting values in a form
|
|
155
166
|
|
|
156
|
-
A
|
|
157
|
-
itself, so a form
|
|
158
|
-
returns to,
|
|
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
|
package/content/iconography.mdx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
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
|
|
14
|
-
Phosphor's own geometry
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
Helix has 108 glyphs from Phosphor, drawn on a 256 box at Phosphor's bold
|
|
14
|
+
weight. We keep Phosphor's own geometry. Rescaling to a 24 box would change the
|
|
15
|
+
optical weight of every glyph, and the set would stop matching Phosphor. We use
|
|
16
|
+
the bold weight because Phosphor's regular weight is a hairline beside the
|
|
17
|
+
medium weight of Helix text. An icon that looks lighter than the words next to
|
|
18
|
+
it does not work as a mark.
|
|
19
19
|
|
|
20
|
-
## Size and color come from the
|
|
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`
|
|
32
|
-
|
|
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
|
|
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
|
|
52
|
-
the
|
|
53
|
-
`label` because it never has
|
|
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
|
-
##
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
page
|
|
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
|
-
##
|
|
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
|
|
76
|
-
markup, in a canvas, or anywhere
|
|
77
|
-
|
|
78
|
-
build
|
|
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
|
-
**
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
93
|
-
set,
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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: 15 kB gzipped for 108 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
|
-
##
|
|
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
|
|
114
|
+
The default is bold. `/bold` is the same set, with the weight in its path.
|
|
111
115
|
|
|
112
|
-
**
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
weight offered
|
|
116
|
-
attribute would put every weight into the index that `name` reads,
|
|
117
|
-
|
|
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
|
|
120
|
-
|
|
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
|
-
##
|
|
126
|
+
## Build your own icon set
|
|
123
127
|
|
|
124
|
-
The
|
|
125
|
-
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
1,404 Phosphor glyphs this set does not
|
|
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`
|
|
148
|
-
bold
|
|
149
|
-
|
|
150
|
-
|
|
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
|
-
**
|
|
153
|
-
|
|
154
|
-
nothing
|
|
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
|
-
##
|
|
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
|
|
159
|
-
resolve from `@phosphor-icons/core` at build time, so adding a glyph
|
|
160
|
-
line
|
|
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
|
-
|
|
163
|
-
|
|
164
|
-
same geometry, a 256 box at the bold weight
|
|
165
|
-
|
|
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
|
-
##
|
|
171
|
+
## Product marks
|
|
168
172
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
**A mark is one rounded body with material removed
|
|
172
|
-
metaphor, because
|
|
173
|
-
|
|
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.
|