@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.
- 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 +431 -0
- package/content/metrics.mdx +296 -299
- 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 +3548 -2682
- package/package.json +3 -3
package/content/motion.mdx
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
2
|
+
description: The duration and easing tokens, and how components handle reduced motion.
|
|
3
3
|
section: Foundations
|
|
4
4
|
title: Motion
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Motion
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Duration and easing tokens
|
|
10
10
|
|
|
11
11
|
[//]: # (tokens:duration)
|
|
12
12
|
|
|
@@ -31,33 +31,40 @@ title: Motion
|
|
|
31
31
|
|
|
32
32
|
[//]: # (/tokens)
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
movement the reader is meant to follow, and `spring` for the few places
|
|
37
|
-
overshoot reads as responsive rather than as a bug.
|
|
34
|
+
Helix has four durations and five curves. Use each curve for one kind of
|
|
35
|
+
movement:
|
|
38
36
|
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
- `standard` for most movement
|
|
38
|
+
- `decelerate` for something arriving
|
|
39
|
+
- `accelerate` for something leaving
|
|
40
|
+
- `emphasized` for a movement you want the user to follow
|
|
41
|
+
- `spring` for the few places where overshoot looks responsive and does not
|
|
42
|
+
look like a bug
|
|
41
43
|
|
|
42
|
-
|
|
44
|
+
The theme engine takes a `motion` intent input. Use it to make every component
|
|
45
|
+
faster or slower at once.
|
|
43
46
|
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
## Each component handles reduced motion
|
|
48
|
+
|
|
49
|
+
The base layer has a global rule that caps animation and transition durations
|
|
50
|
+
when a user has asked for reduced motion. **The global rule does not apply
|
|
51
|
+
inside any component.**
|
|
46
52
|
|
|
47
53
|
```css
|
|
48
54
|
*, *::before, *::after { … }
|
|
49
55
|
```
|
|
50
56
|
|
|
51
|
-
`*` does not cross a shadow boundary.
|
|
52
|
-
layout and the base layer's own recipes
|
|
53
|
-
that draw inside a shadow root. Each
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
The `*` selector does not cross a shadow boundary. The rule applies to your
|
|
58
|
+
prose, your layout, and the base layer's own recipes. It does not apply to the
|
|
59
|
+
ninety-odd components that draw inside a shadow root. Each component that
|
|
60
|
+
animates handles `prefers-reduced-motion` in its own stylesheet. Fifty-nine
|
|
61
|
+
components animate, and all fifty-nine have this rule. A component that
|
|
62
|
+
animates without one fails the build.
|
|
56
63
|
|
|
57
|
-
|
|
58
|
-
global rule
|
|
59
|
-
|
|
60
|
-
noticing.
|
|
64
|
+
The same applies to your own components. If you write a component with a shadow
|
|
65
|
+
root, the global rule does not cover it. This comes from how `*` works in CSS,
|
|
66
|
+
so it applies outside Helix too. It is the easiest accessibility regression to
|
|
67
|
+
ship without noticing.
|
|
61
68
|
|
|
62
69
|
```css
|
|
63
70
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -68,24 +75,25 @@ noticing.
|
|
|
68
75
|
}
|
|
69
76
|
```
|
|
70
77
|
|
|
71
|
-
## What reduced motion
|
|
78
|
+
## What reduced motion changes
|
|
79
|
+
|
|
80
|
+
Some transitions show a relationship, such as a panel that slides out of the
|
|
81
|
+
control that opened it. Others are decorative, such as a floating effect. With
|
|
82
|
+
reduced motion, Helix removes decorative movement. It shortens transitions that
|
|
83
|
+
show a relationship until they are close to instant, without an abrupt jump.
|
|
72
84
|
|
|
73
|
-
|
|
74
|
-
sliding out of the control that opened it, is doing work; a decorative float is
|
|
75
|
-
not. The library's own policy is to remove the second and shorten the first to
|
|
76
|
-
the point where it is instant but not jarring.
|
|
85
|
+
Anything that moves continuously without input stops completely:
|
|
77
86
|
|
|
78
|
-
The
|
|
79
|
-
|
|
80
|
-
|
|
87
|
+
- The aura shader
|
|
88
|
+
- Marquees
|
|
89
|
+
- Looping loaders
|
|
81
90
|
|
|
82
|
-
##
|
|
91
|
+
## Starting styles and view transitions
|
|
83
92
|
|
|
84
|
-
|
|
85
|
-
of its own
|
|
86
|
-
|
|
93
|
+
Overlays use `@starting-style` to animate in. It sets a starting value and needs
|
|
94
|
+
no duration token of its own. The transition that runs from it uses the tokens
|
|
95
|
+
above.
|
|
87
96
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
drive.
|
|
97
|
+
The library does not use view transitions. A view transition runs between two
|
|
98
|
+
states of the document, so a component cannot own one. If you want view
|
|
99
|
+
transitions, start them from your own code.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: The three rules
|
|
2
|
+
description: The three rules for using Helix in any framework, and how each framework applies them.
|
|
3
3
|
parent: installation
|
|
4
4
|
section: Getting started
|
|
5
5
|
title: Other frameworks
|
|
@@ -8,64 +8,66 @@ trailing: true
|
|
|
8
8
|
|
|
9
9
|
# Other frameworks
|
|
10
10
|
|
|
11
|
-
Solid, Qwik, Lit, Ember, Alpine, htmx, and
|
|
12
|
-
document.
|
|
11
|
+
This page covers Solid, Qwik, Lit, Ember, Alpine, htmx, and any other framework
|
|
12
|
+
that puts elements in a document. They all follow the same three rules and need
|
|
13
|
+
nothing else.
|
|
13
14
|
|
|
14
15
|
## The three rules
|
|
15
16
|
|
|
16
17
|
**Strings go through attributes. Everything else goes through properties.** An
|
|
17
|
-
attribute is a string, so an array, an object or a function
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
attribute is a string, so assign an array, an object, or a function to the
|
|
19
|
+
element as a property. Most frameworks do this for you when the element has a
|
|
20
|
+
matching property.
|
|
20
21
|
|
|
21
22
|
```js
|
|
22
23
|
element.items = tabs
|
|
23
24
|
```
|
|
24
25
|
|
|
25
|
-
**
|
|
26
|
-
|
|
27
|
-
lowercase
|
|
28
|
-
|
|
29
|
-
should end up passing that string through.
|
|
26
|
+
**Bind events with `addEventListener`, using the event's own name.** Helix event
|
|
27
|
+
names, such as `helix-before-open`, `helix-before-change`, and `helix-select`,
|
|
28
|
+
are lowercase and hyphenated. They have no camel-case variant. Your framework's
|
|
29
|
+
`on*` syntax needs to pass that exact name through.
|
|
30
30
|
|
|
31
31
|
```js
|
|
32
32
|
element.addEventListener('helix-before-change', (event) => console.log(event.detail))
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
**A declared `value`, `open`, `checked
|
|
36
|
-
starts
|
|
37
|
-
To
|
|
38
|
-
`preventDefault()
|
|
39
|
-
`default-value`, `default-open
|
|
40
|
-
[State and control](./state-and-control)
|
|
35
|
+
**A declared `value`, `open`, `checked`, or `active` is a seed.** The element
|
|
36
|
+
starts with that value and then manages its own state, as `<input value="Ada">`
|
|
37
|
+
does. To refuse one change, listen for the matching `helix-before-*` event and
|
|
38
|
+
call `preventDefault()`. To own the state fully, refuse every change and assign
|
|
39
|
+
your own value. A reset returns to `default-value`, `default-open`, and the
|
|
40
|
+
similar attributes. [State and control](./state-and-control) explains the model
|
|
41
|
+
in full.
|
|
41
42
|
|
|
42
|
-
##
|
|
43
|
+
## Notes for each framework
|
|
43
44
|
|
|
44
45
|
| Framework | Notes |
|
|
45
46
|
|---|---|
|
|
46
47
|
| Solid | Sets properties on custom elements by default. Use `prop:` to force one and `attr:` to force the other. `on:helix-before-change` binds the literal name |
|
|
47
48
|
| Qwik | Works as JSX. Bind with `document:on…` or a ref, since a plain `on*` prop is normalized |
|
|
48
|
-
| Lit | `.prop=`, `?bool
|
|
49
|
+
| Lit | `.prop=`, `?bool=`, and `@helix-before-change=` work with no setup |
|
|
49
50
|
| Ember | Angle-bracket invocation passes attributes only, so set properties from a modifier |
|
|
50
|
-
| Alpine, htmx | Markup and attributes only, which
|
|
51
|
+
| Alpine, htmx | Markup and attributes only, which work with every component. Refusing a change needs script |
|
|
51
52
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
We do not test these frameworks, so they share one page. If your framework needs
|
|
54
|
+
something the three rules do not cover, tell us. It is a gap in this page or in
|
|
55
|
+
the library.
|
|
55
56
|
|
|
56
57
|
## Typings
|
|
57
58
|
|
|
58
|
-
`@fusion.dev/helix/jsx` is the base for
|
|
59
|
-
typings are built from it. If your framework needs a different
|
|
60
|
-
manifest
|
|
61
|
-
prop, event and part,
|
|
59
|
+
`@fusion.dev/helix/jsx` is the base for typing any framework that uses JSX, and
|
|
60
|
+
the Svelte typings are built from it. If your framework needs a different
|
|
61
|
+
format, use the manifest at `@fusion.dev/helix/custom-elements.json`. It
|
|
62
|
+
describes every tag, prop, event, and part, and we generate all our typings from
|
|
63
|
+
it.
|
|
62
64
|
|
|
63
|
-
##
|
|
65
|
+
## Register the elements
|
|
64
66
|
|
|
65
67
|
```ts
|
|
66
68
|
import '@fusion.dev/helix'
|
|
67
69
|
```
|
|
68
70
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
Import it once, anywhere that runs in the browser. The stylesheets are the same
|
|
72
|
+
in every framework, and they are the part most often left out. See
|
|
73
|
+
[Installation](./installation) for the stylesheets.
|
package/content/overlays.mdx
CHANGED
|
@@ -1,71 +1,90 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
2
|
+
description: How overlays use the top layer, how they close, and where focus goes.
|
|
3
3
|
section: Essentials
|
|
4
4
|
title: Overlays and layering
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Overlays and layering
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
`Megamenu`, `Speed Dial`, `Command` and `Tour` are all overlays, and they all
|
|
11
|
-
work the same way underneath. Knowing that way saves you from most of the
|
|
12
|
-
problems overlays cause.
|
|
9
|
+
These components are overlays, and they all work the same way:
|
|
13
10
|
|
|
14
|
-
|
|
11
|
+
- `Dialog`
|
|
12
|
+
- `Drawer`
|
|
13
|
+
- `Popover`
|
|
14
|
+
- `Menu`
|
|
15
|
+
- `Select`
|
|
16
|
+
- `Combobox`
|
|
17
|
+
- `Tooltip`
|
|
18
|
+
- `Megamenu`
|
|
19
|
+
- `Speed Dial`
|
|
20
|
+
- `Command`
|
|
21
|
+
- `Tour`
|
|
15
22
|
|
|
16
|
-
|
|
17
|
-
everything, regardless of `z-index`, `overflow: hidden`, `transform` or a
|
|
18
|
-
stacking context somewhere up the tree.
|
|
23
|
+
Knowing how they work helps you avoid most overlay problems.
|
|
19
24
|
|
|
20
|
-
|
|
21
|
-
scrolling panel does not get clipped. A tooltip inside a `transform`ed card does
|
|
22
|
-
not get trapped in its stacking context. There is no portal to configure and no
|
|
23
|
-
`appendTo` to pass, because the platform is doing it.
|
|
25
|
+
## Overlays render in the top layer
|
|
24
26
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
else is wrong.
|
|
27
|
+
Every overlay uses the popover API, so the browser draws it in the top layer.
|
|
28
|
+
The top layer is above everything on the page, and these do not affect it:
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
- `z-index`
|
|
31
|
+
- `overflow: hidden`
|
|
32
|
+
- `transform`
|
|
33
|
+
- a stacking context in an ancestor
|
|
30
34
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
space. Anchor positioning would be the platform answer and is not yet available
|
|
35
|
-
everywhere the rest of the library's requirements are. See [Browser
|
|
36
|
-
support](./browser-support).
|
|
35
|
+
As a result, a menu inside a scrolling panel is not clipped, and a tooltip
|
|
36
|
+
inside a `transform`ed card is not trapped in its stacking context. You do not
|
|
37
|
+
configure a portal or pass `appendTo`, because the browser handles the layering.
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
**Do not raise an overlay with `z-index`.** It has no effect on a top-layer
|
|
40
|
+
element. If you want to use it, something else is wrong.
|
|
40
41
|
|
|
41
|
-
##
|
|
42
|
+
## How overlays are positioned
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
adds is making that correct across shadow boundaries.
|
|
44
|
+
Overlays are placed with `@floating-ui/dom`, which does the following:
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
- It flips a panel to the other side of its anchor when there is no room below.
|
|
47
|
+
- It moves a panel placed beside its anchor below or above when there is no room
|
|
48
|
+
on either side.
|
|
49
|
+
- It shifts a panel to stay on screen.
|
|
50
|
+
- It sizes a panel to the available space.
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
leaves the rest.
|
|
52
|
+
CSS anchor positioning is the platform's own way to do this. We do not use it
|
|
53
|
+
yet, because it is not available in every browser the rest of Helix supports.
|
|
54
|
+
See [Browser support](./browser-support).
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
While an overlay is open, its position is recomputed on scroll and resize. The
|
|
57
|
+
recomputing stops when the overlay closes.
|
|
57
58
|
|
|
58
|
-
|
|
59
|
-
whatever opened it. `Dialog` and `Drawer` do this; `Popover` does it in its
|
|
60
|
-
modal mode and not otherwise.
|
|
59
|
+
## How overlays close
|
|
61
60
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
something with a button in it, that is a `Popover`.
|
|
61
|
+
Light dismiss comes from the platform: click outside the overlay, or press
|
|
62
|
+
`Escape`. Helix makes light dismiss work correctly across shadow boundaries.
|
|
65
63
|
|
|
66
|
-
|
|
64
|
+
`document.activeElement` stops at the outermost host. Focus inside a shadow tree
|
|
65
|
+
is reported as the element that contains the tree. A simple check for whether
|
|
66
|
+
focus is still inside the overlay never matches, so the overlay would close as
|
|
67
|
+
soon as anything inside it is focused. Every way of closing an overlay in Helix
|
|
68
|
+
checks focus by walking through the shadow roots.
|
|
67
69
|
|
|
68
|
-
|
|
70
|
+
Nested overlays work because lifecycle events do not bubble.
|
|
71
|
+
`helix-before-open` and `helix-before-close` do not bubble and are not composed,
|
|
72
|
+
so a menu that closes inside a dialog does not close the dialog. `Escape` closes
|
|
73
|
+
the innermost overlay and leaves the others open.
|
|
74
|
+
|
|
75
|
+
## Where focus goes
|
|
76
|
+
|
|
77
|
+
A modal overlay keeps `Tab` inside itself while it is open, and it returns focus
|
|
78
|
+
to the element that opened it. `Dialog` and `Drawer` always do this. `Popover`
|
|
79
|
+
does it only in its modal mode.
|
|
80
|
+
|
|
81
|
+
**A tooltip never takes focus.** A tooltip only describes a control, so it
|
|
82
|
+
cannot contain a button. For something with a button in it, use a `Popover`.
|
|
83
|
+
|
|
84
|
+
## Keep an overlay open
|
|
85
|
+
|
|
86
|
+
An overlay sends `helix-before-close` before it closes. Cancel the event to keep
|
|
87
|
+
the overlay open:
|
|
69
88
|
|
|
70
89
|
```ts
|
|
71
90
|
dialog.addEventListener('helix-before-close', (event) => {
|
|
@@ -75,20 +94,19 @@ dialog.addEventListener('helix-before-close', (event) => {
|
|
|
75
94
|
})
|
|
76
95
|
```
|
|
77
96
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
control](./state-and-control).
|
|
97
|
+
Canceling covers `Escape`, a backdrop click, and the close button, because all
|
|
98
|
+
three send the same request. See [State and control](./state-and-control).
|
|
81
99
|
|
|
82
100
|
## Entry and exit animation
|
|
83
101
|
|
|
84
|
-
Overlays animate in with `@starting-style
|
|
85
|
-
`overlay` and `display` properties, which
|
|
86
|
-
element the browser is about to remove from the top layer. Every
|
|
87
|
-
`prefers-reduced-motion`.
|
|
102
|
+
Overlays animate in with `@starting-style`. They animate out with a transition
|
|
103
|
+
on the `overlay` and `display` properties, which allows an exit animation on an
|
|
104
|
+
element the browser is about to remove from the top layer. Every overlay
|
|
105
|
+
responds to `prefers-reduced-motion`.
|
|
88
106
|
|
|
89
|
-
##
|
|
107
|
+
## Choose an overlay
|
|
90
108
|
|
|
91
|
-
| | |
|
|
109
|
+
| Component | Use it for |
|
|
92
110
|
|---|---|
|
|
93
111
|
| `Dialog` | A task that blocks the page |
|
|
94
112
|
| `Drawer` | The same, from an edge, usually larger |
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
2
|
+
description: Change a component's styles with tokens, per-component hooks, or parts, and choose which one to use.
|
|
3
3
|
section: Theming
|
|
4
4
|
title: Overriding a component
|
|
5
5
|
---
|
|
@@ -7,11 +7,10 @@ title: Overriding a component
|
|
|
7
7
|
# Overriding a component
|
|
8
8
|
|
|
9
9
|
Every Helix component draws inside a shadow root, so your stylesheet cannot
|
|
10
|
-
reach
|
|
11
|
-
|
|
12
|
-
page.
|
|
10
|
+
reach the elements inside it. Your rules cannot break a component's internals
|
|
11
|
+
by accident, and we can rewrite a component without breaking your page.
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
You can change a component's styles in three ways. Try them in the order below.
|
|
15
14
|
|
|
16
15
|
## 1. Tokens
|
|
17
16
|
|
|
@@ -21,15 +20,17 @@ Three doors are open, and they are in the order you should try them.
|
|
|
21
20
|
}
|
|
22
21
|
```
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
properties inherit, so one declaration reaches every component at once.
|
|
23
|
+
To make every card, dialog, and button squarer, change a token. Set the seed.
|
|
24
|
+
The four radius steps, such as `--helix-radii-md`, are derived from it. `md` is
|
|
25
|
+
one step of the four, and buttons and dialogs use a different step. See
|
|
26
|
+
[Shape and borders](./shape-and-borders).
|
|
29
27
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
Custom properties inherit, so tokens cross shadow boundaries. One declaration
|
|
29
|
+
reaches every component at once.
|
|
30
|
+
|
|
31
|
+
Try tokens first. A request to restyle the button is often a brand difference,
|
|
32
|
+
such as less rounding. A token is the smaller change, and it keeps every
|
|
33
|
+
component consistent.
|
|
33
34
|
|
|
34
35
|
See [Tokens](./tokens) and [Deriving a theme](./theming).
|
|
35
36
|
|
|
@@ -45,15 +46,19 @@ helix-accordion {
|
|
|
45
46
|
}
|
|
46
47
|
```
|
|
47
48
|
|
|
48
|
-
A hook is a custom property a component publishes and reads. It defaults
|
|
49
|
-
semantic token, so
|
|
50
|
-
changes that component
|
|
49
|
+
A hook is a custom property that a component publishes and reads. It defaults
|
|
50
|
+
to a semantic token, so a hook you leave unset uses the themed value. Setting a
|
|
51
|
+
hook changes that one component.
|
|
52
|
+
|
|
53
|
+
**Hooks are component API.** We check that they stay separate from tokens. A
|
|
54
|
+
hook is named after its component, such as `--card-padding` or
|
|
55
|
+
`--drawer-radius`. A token is named `--helix-*`. We also check each hook in both
|
|
56
|
+
directions:
|
|
51
57
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
and read and never declared is a working feature nobody is told about.
|
|
58
|
+
- A rule in the component reads every documented hook. A hook that is declared
|
|
59
|
+
and never read would do nothing.
|
|
60
|
+
- Every hook that a rule reads is documented. Otherwise you would not know the
|
|
61
|
+
hook exists.
|
|
57
62
|
|
|
58
63
|
Each component's page lists its hooks under Custom properties.
|
|
59
64
|
|
|
@@ -69,23 +74,23 @@ helix-dialog::part(dialog) {
|
|
|
69
74
|
}
|
|
70
75
|
```
|
|
71
76
|
|
|
72
|
-
`::part()`
|
|
73
|
-
declaration
|
|
74
|
-
|
|
75
|
-
internal structure.
|
|
77
|
+
`::part()` selects a named element inside the shadow root, and you can write any
|
|
78
|
+
declaration on it. Parts give you the most control. Try them last, because they
|
|
79
|
+
tie your stylesheet to a component's internal structure.
|
|
76
80
|
|
|
77
|
-
**
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
+
**Every documented part is rendered.** We check parts in both directions. A
|
|
82
|
+
documented part must be rendered, and a rendered part must be documented. A
|
|
83
|
+
documented part that nothing renders would be the worse failure for you. A
|
|
84
|
+
`::part()` that matches nothing is still valid CSS, so you get no error.
|
|
81
85
|
|
|
82
86
|
Each component's page lists its parts.
|
|
83
87
|
|
|
84
|
-
###
|
|
88
|
+
### Parts of a component inside another component
|
|
85
89
|
|
|
86
|
-
`::part()`
|
|
87
|
-
|
|
88
|
-
|
|
90
|
+
`::part()` crosses only one shadow boundary. When a component renders another
|
|
91
|
+
component, the child's parts would be out of reach. Every such component
|
|
92
|
+
forwards its child's parts, and each forwarded name starts with the role the
|
|
93
|
+
child plays:
|
|
89
94
|
|
|
90
95
|
```css
|
|
91
96
|
helix-date-input::part(picker-cell) { … }
|
|
@@ -93,34 +98,39 @@ helix-select::part(listbox-items) { … }
|
|
|
93
98
|
helix-video::part(overflow-header) { … }
|
|
94
99
|
```
|
|
95
100
|
|
|
96
|
-
`<helix-date-input>` renders a date picker
|
|
97
|
-
`cell` is `picker-cell
|
|
98
|
-
|
|
99
|
-
its arrow is `tooltip-arrow`.
|
|
100
|
-
|
|
101
|
-
A part that changes with a state
|
|
102
|
-
that name is forwarded too. A tooltip
|
|
103
|
-
`tooltip-bottom`, and its arrow `tooltip-arrow-bottom
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
101
|
+
`<helix-date-input>` renders a date picker in the `picker` role, so the picker's
|
|
102
|
+
`cell` part is `picker-cell`. When the child's part is the role itself, the
|
|
103
|
+
forwarded name is the role alone. The tooltip inside `<helix-copy-button>` is
|
|
104
|
+
`tooltip`, and its arrow is `tooltip-arrow`.
|
|
105
|
+
|
|
106
|
+
A part that changes with a state has a second name while the state is active,
|
|
107
|
+
and that name is forwarded too. A tooltip placed below its button is also
|
|
108
|
+
`tooltip-bottom`, and its arrow is also `tooltip-arrow-bottom`. Your stylesheet
|
|
109
|
+
can use these names to style each side, which it cannot detect any other way.
|
|
110
|
+
The arrow already faces the button on every side. Use the names for styles you
|
|
111
|
+
add on top.
|
|
112
|
+
|
|
113
|
+
**The prefix keeps part names unique.** A component and its child can use the
|
|
114
|
+
same word. This happened once: `Command` forwarded the listbox's `search-field`
|
|
115
|
+
under a name it also rendered itself, so `::part(search-field)` matched two
|
|
116
|
+
elements at once.
|
|
117
|
+
|
|
118
|
+
We check forwarding in two ways:
|
|
119
|
+
|
|
120
|
+
- `helix component`'s `parts` matches every published part with a rendered one,
|
|
121
|
+
and every rendered part with a published one.
|
|
122
|
+
- `helix component`'s `exportparts` requires a composed component to forward
|
|
123
|
+
every part its child publishes, under one prefix, with no collisions.
|
|
124
|
+
|
|
125
|
+
As a result, you can reach every part the color picker publishes from inside a
|
|
115
126
|
color input, and every part the date picker publishes from inside a date input.
|
|
116
127
|
|
|
117
|
-
If you
|
|
118
|
-
working around: the missing part is the bug.
|
|
128
|
+
If you find a part you cannot reach, report it to us. The missing part is a bug.
|
|
119
129
|
|
|
120
|
-
###
|
|
130
|
+
### Part names shared by every component
|
|
121
131
|
|
|
122
|
-
|
|
123
|
-
you learn
|
|
132
|
+
Some parts name a role. Every component uses the same fixed set of role names,
|
|
133
|
+
so you learn each name once:
|
|
124
134
|
|
|
125
135
|
| Part | What it is |
|
|
126
136
|
|---|---|
|
|
@@ -131,32 +141,33 @@ you learn it once instead of per component:
|
|
|
131
141
|
| `surface` | A floating panel drawn over the page. |
|
|
132
142
|
| `viewport` | A scrollport the component reads the position of. |
|
|
133
143
|
|
|
134
|
-
|
|
135
|
-
`weekday`, is that component's
|
|
144
|
+
A part that names something specific to one component, such as `thumb`,
|
|
145
|
+
`swatch`, or `weekday`, is listed on that component's page.
|
|
136
146
|
|
|
137
147
|
## What not to do
|
|
138
148
|
|
|
139
|
-
**Do not raise specificity to
|
|
140
|
-
still outside the shadow root, so it
|
|
141
|
-
|
|
149
|
+
**Do not raise specificity to override a component.** `helix-button.thing.thing`
|
|
150
|
+
is still outside the shadow root, so it reaches nothing that `helix-button` did
|
|
151
|
+
not already reach. The shadow boundary blocks your rule, and selector weight
|
|
152
|
+
cannot change that.
|
|
142
153
|
|
|
143
154
|
**Do not restyle through `!important` on the host.** Some properties inherit
|
|
144
|
-
into a shadow root and most do not
|
|
145
|
-
and fails
|
|
146
|
-
|
|
155
|
+
into a shadow root and most do not. `!important` on the host works for `color`
|
|
156
|
+
and `font-family` and fails with no error for everything else. A silent failure
|
|
157
|
+
is harder to find than an error.
|
|
147
158
|
|
|
148
|
-
**Do not fork the component to change a color.** The headless tier
|
|
149
|
-
|
|
150
|
-
|
|
159
|
+
**Do not fork the component to change a color.** The headless tier is for when
|
|
160
|
+
you want none of our styling. Changing one value does not need it. See
|
|
161
|
+
[Tiers](./tiers).
|
|
151
162
|
|
|
152
|
-
##
|
|
163
|
+
## Replace all of the styling
|
|
153
164
|
|
|
154
165
|
```ts
|
|
155
166
|
import '@fusion.dev/helix-headless'
|
|
156
167
|
```
|
|
157
168
|
|
|
158
|
-
The headless tier
|
|
159
|
-
no CSS
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
169
|
+
The headless tier provides structure, accessibility, state, and form
|
|
170
|
+
participation, with no CSS. You write the entire appearance. Use it when your
|
|
171
|
+
design is entirely different from ours. The headless tier is fully supported.
|
|
172
|
+
For example, `helix-headless-select` is a registered element that you can use
|
|
173
|
+
directly.
|