@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
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: What to install, the order the stylesheets go in, and how to check it worked.
2
+ description: What to install, the order of the stylesheets, and how to check that the elements registered.
3
3
  order: 3
4
4
  section: Getting started
5
5
  title: Installation
@@ -11,55 +11,59 @@ title: Installation
11
11
  bun add @fusion.dev/helix
12
12
  ```
13
13
 
14
- `@fusion.dev/helix-styles` is not optional and not a peer dependency by accident. The
15
- components reference semantic tokens and paint nothing without them: a Helix
16
- page with the elements installed and the tokens missing renders unstyled boxes
17
- rather than an unthemed version of itself.
14
+ `@fusion.dev/helix-styles` is a required dependency, on purpose. The components
15
+ use its semantic tokens and draw nothing without them. If the elements are
16
+ installed and the tokens are missing, your page shows unstyled boxes. There is
17
+ no fallback theme.
18
18
 
19
- ## The stylesheets go in this order
19
+ ## Import the stylesheets
20
20
 
21
21
  ```ts
22
22
  import '@fusion.dev/helix-styles/helix.css'
23
23
  ```
24
24
 
25
- One import, and it is four files in the order they have to be in:
26
- `primitives.css`, `tokens.css`, `base.css`, `elements.css`. The order is
27
- load-bearing and the failure is silent, so it is stated once inside the package
28
- rather than by every consumer.
25
+ This one import loads four files in the order they must load:
29
26
 
30
- It resolves `@fusion.dev/helix-styles` through this package's own dependency, so
31
- installing `@fusion.dev/helix` is enough. A bare specifier in an `@import` is
32
- resolved by a bundler and never by a browser, so a page serving CSS through a
33
- `<link>` takes the four by path instead.
27
+ 1. `primitives.css`
28
+ 2. `tokens.css`
29
+ 3. `base.css`
30
+ 4. `elements.css`
34
31
 
35
- ### The four, for anyone substituting a layer
32
+ The order matters, and a wrong order fails without an error. We set the order
33
+ once, inside the package, so you do not have to.
36
34
 
37
- Each file keeps its own export, so a consumer bringing their own reset or their
38
- own ramp imports the two they want and skips the bundle.
35
+ The import resolves `@fusion.dev/helix-styles` through the dependency in
36
+ `@fusion.dev/helix`, so installing `@fusion.dev/helix` is enough. Only a bundler
37
+ resolves a bare specifier like this one. If you serve CSS through a `<link>`,
38
+ link `helix.css` by its path, as [Without a bundler](./without-a-bundler)
39
+ shows.
39
40
 
40
- - **`primitives.css`** is the raw ramp the semantic layer is derived from, and
41
- it is where the seven `--helix-seed-*` knobs are read. Nothing in your own
42
- code should name a primitive, and leaving the file out is not a way to
43
- enforce that: `tokens.css` refers to one 297 times, so without it every
44
- semantic token resolves to nothing at all. Measured on a page missing it,
41
+ ### Import the files one at a time
42
+
43
+ Each file has its own export. If you bring your own reset or your own ramp,
44
+ import the two files you want and skip `helix.css`.
45
+
46
+ - **`primitives.css`** holds the raw ramp that the semantic layer is derived
47
+ from. The seven `--helix-seed-*` properties are read here. Your own code
48
+ should not name a primitive, and leaving the file out does not enforce that.
49
+ `tokens.css` refers to a primitive 297 times, so without `primitives.css`
50
+ every semantic token resolves to nothing. We measured a page without it:
45
51
  `--helix-color-background` computes to `rgba(0, 0, 0, 0)`, and so does every
46
- other color. It is also the file [theming](theming.mdx) depends on, since a
47
- `--helix-seed-brand` nobody reads moves nothing.
48
- - **`tokens.css`** declares the semantic layer, 185 custom properties in 15
49
- categories, plus `color-scheme: light dark` on `:root`. Everything below reads
50
- from it, and it reads primitives.
51
- - **`base.css`** is the element reset, the type scale on bare `h1` through
52
- `h6`, and the recipes for markup this library does not render. It reads
53
- tokens.
54
-
55
- **The last one is the one people leave out**, because everything works without
56
- it right up until the page is server-rendered: an element that has not upgraded
57
- yet has no shadow root and no styles of its own, so the markup flashes unstyled
58
- and then snaps into place. `elements.css` is what makes that interval look like
59
- the finished page.
60
-
61
- Put them in a stylesheet you control rather than deep inside a component, so the
62
- order is visible in one place and stays that way.
52
+ other color. [Theming](theming.mdx) also depends on this file, because without
53
+ it, setting `--helix-seed-brand` changes nothing.
54
+ - **`tokens.css`** declares the semantic layer: 185 custom properties in 15
55
+ categories, plus `color-scheme: light dark` on `:root`. The files below read
56
+ from it, and it reads from `primitives.css`.
57
+ - **`base.css`** holds the element reset, the type scale on bare `h1` through
58
+ `h6`, and the recipes for markup that Helix does not render. It reads tokens.
59
+ - **`elements.css`** styles elements before they upgrade. It is the file most
60
+ often left out, because a page works without it until the page is
61
+ server-rendered. An element that has not upgraded has no shadow root and no
62
+ styles of its own, so the markup flashes unstyled and then jumps into place.
63
+ `elements.css` makes that interval look like the finished page.
64
+
65
+ Import the files from a stylesheet you control, and keep them out of individual
66
+ components. The order then stays visible in one place.
63
67
 
64
68
  ## Register the elements
65
69
 
@@ -67,57 +71,62 @@ order is visible in one place and stays that way.
67
71
  import '@fusion.dev/helix'
68
72
  ```
69
73
 
70
- A side-effect import. It calls `customElements.define` for every element, once,
71
- and is safe to run more than once: the helper behind it checks the registry
72
- first, because a second `define` for the same tag throws and takes the rest of
73
- the module with it.
74
+ This import has a side effect: it calls `customElements.define` once for every
75
+ element. You can run it more than once. A second `define` for the same tag
76
+ throws an error and stops the rest of the module, so the helper behind the
77
+ import checks the registry first.
74
78
 
75
- You can register a subset instead, from a subpath:
79
+ To register only some elements, import them from subpaths:
76
80
 
77
81
  ```ts
78
82
  import '@fusion.dev/helix/button'
79
83
  import '@fusion.dev/helix/dialog'
80
84
  ```
81
85
 
82
- Worth doing when you use a handful of components. Not worth doing in the belief
83
- that the full import ships everything to the browser: the package is
84
- side-effectful by design, so a bundler keeps what you import either way, and the
85
- subpaths are about being explicit rather than about size.
86
+ Import from subpaths when you use a few components. The package has side
87
+ effects by design, so a bundler keeps everything you import. The whole-package
88
+ import registers every element, and a subpath registers only its family.
86
89
 
87
- ## Check it worked
90
+ ## Check the installation
88
91
 
89
- Render one element and ask the browser whether it upgraded:
92
+ Render one element and check whether it upgraded:
90
93
 
91
94
  ```ts
92
95
  await customElements.whenDefined('helix-button')
93
96
  console.log(document.querySelector('helix-button')?.shadowRoot !== null)
94
97
  ```
95
98
 
96
- `true` means the definition arrived and the element built its shadow root.
99
+ `true` means the definition loaded and the element built its shadow root.
97
100
  `false`, or a promise that never settles, means the registration import did not
98
- run: usually a `'use client'` boundary the import ended up outside of, or a
99
- subpath import for a component you did not end up using.
101
+ run. Usually the import is outside a `'use client'` boundary, or you imported
102
+ the subpath of a component you did not end up using.
103
+
104
+ If the element upgraded and the page still looks wrong, check the stylesheets
105
+ in the order above. The symptom tells you which layer is missing:
106
+
107
+ - No color at all means `tokens.css` is missing.
108
+ - Unstyled prose and bare headings mean `base.css` is missing.
109
+ - Components that look right once JavaScript runs, but flash unstyled before
110
+ it, mean `elements.css` is missing.
100
111
 
101
- **A styling problem looks nothing like that.** If the element upgraded and the
102
- page still looks wrong, the stylesheets are the thing to check, in the order
103
- above. The tell is which layer is missing: no color at all is `tokens.css`,
104
- unstyled prose and bare headings is `base.css`, and components that look right
105
- once JavaScript runs but flash unstyled before it is `elements.css`.
112
+ ## Framework setup
106
113
 
107
- ## Your framework
114
+ The elements are custom elements, and every framework can render them.
115
+ Frameworks differ in three ways:
108
116
 
109
- The elements are custom elements and every framework can render one. What
110
- differs is how a framework passes a non-string value, how it binds an event, and
111
- whether it needs telling that an unknown tag is deliberate.
117
+ - How they pass a value that is not a string.
118
+ - How they bind an event.
119
+ - Whether they need configuration to accept an unknown tag.
112
120
 
113
121
  | | |
114
122
  |---|---|
115
- | [React](./react) | A runtime bridge ships for it, plus `jsx.d.ts` |
123
+ | [React](./react) | Ships a runtime bridge and `jsx.d.ts` |
116
124
  | [Vue](./vue) | Ships `vue.d.ts`, needs `compilerOptions.isCustomElement` |
117
- | [Svelte](./svelte) | Ships `svelte.d.ts`, and needs nothing else |
125
+ | [Svelte](./svelte) | Ships `svelte.d.ts`, and needs no configuration |
118
126
  | [Angular](./angular) | Needs `CUSTOM_ELEMENTS_SCHEMA` |
119
- | [Other frameworks](./other-frameworks) | Solid, Qwik, Lit, and the three rules they all reduce to |
120
- | [Without a bundler](./without-a-bundler) | An import map, or the stylesheets alone |
127
+ | [Other frameworks](./other-frameworks) | Solid, Qwik, Lit, and the three rules they all follow |
128
+ | [Without a bundler](./without-a-bundler) | The stylesheets with no build step |
121
129
 
122
- A framework gets its own page when Helix ships typings for it, or when it
123
- refuses to compile without configuration. Everything else works with no setup.
130
+ A framework has its own page when Helix ships typings for it, or when it does
131
+ not compile Helix tags without configuration. Other frameworks work with no
132
+ setup.
@@ -1,13 +1,13 @@
1
1
  ---
2
- description: Every input the theme engine takes, and what each one decides.
2
+ description: Every input the theme engine takes, and what each one changes.
3
3
  section: Theming
4
4
  title: Intent reference
5
5
  ---
6
6
 
7
7
  # Intent reference
8
8
 
9
- [Deriving a theme](./theming) is the argument. This is the surface: everything
10
- `ThemeIntent` accepts, and what changes when you set it.
9
+ [Deriving a theme](./theming) explains how the engine works. This page lists
10
+ every field `ThemeIntent` accepts and what changes when you set it.
11
11
 
12
12
  ```ts
13
13
  import { derive } from '@fusion.dev/helix-theme'
@@ -15,93 +15,98 @@ import { derive } from '@fusion.dev/helix-theme'
15
15
  const theme = derive({ brand: '#2563eb' })
16
16
  ```
17
17
 
18
- **`brand` is the only required input.** Everything else has a default that is
19
- derived from it or from the design language, and a theme that sets one field is
20
- a valid theme.
18
+ **`brand` is the only required input.** Every other field has a default,
19
+ computed from `brand` or from the Helix design language. A theme that sets only
20
+ `brand` is a valid theme.
21
21
 
22
22
  ## Color
23
23
 
24
- | Field | Decides |
24
+ | Field | What it controls |
25
25
  |---|---|
26
- | `brand` | The one color most consumers care about. Required |
27
- | `accent` | The second emphasis. Unnamed, it is the brand; `{ from: 'brand', rotate: 180 }` asks for the complement |
26
+ | `brand` | Your main brand color. Required |
27
+ | `accent` | The second emphasis color. Defaults to the brand color. `{ from: 'brand', rotate: 180 }` gives the complementary color |
28
28
  | `neutral` | The gray ramp, which can be tinted toward the brand |
29
- | `status` | `info`, `success`, `warning`, `danger`. A color pins one; an object nudges its hue within the meaning it already carries |
29
+ | `status` | `info`, `success`, `warning`, and `danger`. A color pins one. An object adjusts its hue while it keeps its meaning |
30
30
  | `chroma` | How saturated the palette is, independent of hue |
31
- | `scheme` | `'light'` or `'dark'`, for a theme that is only one |
32
- | `tokens` | Literal overrides, by token name, for the cases nothing else reaches |
31
+ | `scheme` | `'light'` or `'dark'`, for a theme that has only one mode |
32
+ | `tokens` | Literal overrides by token name, for values no other field sets |
33
33
 
34
- **`accent` may be a relation and `status` may not**, and the asymmetry is
35
- deliberate: a `danger` that rotates with the brand is a warning a reader has to
36
- re-learn per product.
34
+ `accent` can be defined relative to the brand. `status` colors cannot. If
35
+ `danger` rotated with the brand, your users would have to learn a new warning
36
+ color for each product.
37
37
 
38
- **`chroma` is the axis a brand's feel lives on.** Monotone, muted, restrained
39
- and vivid are all chroma statements and none of them is a hue. `ceiling` clamps,
40
- so 0 is monotone and roughly 0.10 is muted; `scale` multiplies what the brand
41
- supplies. Both apply to every derived color including the brand itself, because
42
- a monotone theme is one where the brand is gray too. Never applied to a value
43
- you pinned: a pinned color is a fact.
38
+ `chroma` sets how saturated the theme feels. Monotone, muted, restrained, and
39
+ vivid all describe chroma, and none of them describes a hue. `chroma` has two
40
+ settings:
44
41
 
45
- ## The guarantee
42
+ - `ceiling` caps chroma. 0 is monotone, and about 0.10 is muted.
43
+ - `scale` multiplies the chroma the brand color supplies.
46
44
 
47
- | Field | Decides |
45
+ Both apply to every derived color, including the brand color, because in a
46
+ monotone theme the brand is gray too. Neither applies to a color you pinned. A
47
+ pinned color is used exactly as you set it.
48
+
49
+ ## Contrast
50
+
51
+ | Field | What it controls |
48
52
  |---|---|
49
- | `contrastTarget` | Target contrast for body text. 4.5 is WCAG AA, 7 is AAA |
50
- | `pairings` | Extra foreground and background pairs to solve for, beyond the model's own |
53
+ | `contrastTarget` | Target contrast for body text. 4.5 is WCAG AA, and 7 is AAA |
54
+ | `pairings` | Extra foreground and background pairs to solve, in addition to the pairs in the engine's surface model |
51
55
 
52
- 4.5 is the default because the European Accessibility Act points at WCAG 2.1 AA,
53
- so it is the figure with legal weight.
56
+ The default is 4.5, the WCAG 2.1 AA figure. The European Accessibility Act
57
+ refers to WCAG 2.1 AA, so 4.5 is the figure with legal weight.
54
58
 
55
59
  ## Shape and space
56
60
 
57
- | Field | Decides |
61
+ | Field | What it controls |
58
62
  |---|---|
59
63
  | `radius` | Corner rounding across the system |
60
- | `density` | Scales spacing and control heights. 1 is default, 0.875 is compact |
61
- | `separation` | Whether a surface is told apart by an edge or by a lift |
64
+ | `density` | Scales spacing and control heights. 1 is the default, and 0.875 is compact |
65
+ | `separation` | Whether a surface is set apart by an edge or by a lift |
62
66
 
63
- `separation` is the policy pair described in [Surfaces and
67
+ `separation` chooses between the two policies described in [Surfaces and
64
68
  separation](./surfaces-and-separation).
65
69
 
66
70
  ## Type and motion
67
71
 
68
- | Field | Decides |
72
+ | Field | What it controls |
69
73
  |---|---|
70
- | `font` | The families, and the half of the type system that is a scale |
74
+ | `font` | The font families and the type scale |
71
75
  | `motion` | `character` and `scale` |
72
76
 
73
- **`motion.character` sets the curve every component reaches for and carries a
74
- duration multiplier with it**, because the two are one decision: a spring over
75
- 120ms is a twitch, and a soft curve over 120ms is a fade. `scale` multiplies on
76
- top for a theme that wants the character and not the pace.
77
+ `motion.character` sets the easing curve that every component uses, along with
78
+ a duration multiplier. The curve and the duration are set together because they
79
+ depend on each other. A spring over 120ms looks like a twitch, and a soft curve
80
+ over 120ms looks like a fade. `scale` multiplies the durations again, so you can
81
+ keep a character and change its speed.
77
82
 
78
- `instant` is not scaled. It is 0 and means "do not animate", which is a
79
- statement rather than a duration, and it emits no curves at all: three easings
80
- for transitions of zero length is a contradiction.
83
+ The `instant` character is 0, which means "do not animate". `scale` does not
84
+ change it. It outputs none of the three easing curves, because a transition of
85
+ zero length has no curve to follow.
81
86
 
82
- `precise` is the one character that is not physical. The other three accelerate,
83
- settle or overshoot; linear reads as moved by a mechanism, and it is the only
84
- curve legible on a short travel, because constancy shows in the timing where an
85
- overshoot shows in the distance.
87
+ `precise` is the only character that does not imitate physical motion. The
88
+ other three accelerate, settle, or overshoot. `precise` is linear, so movement
89
+ looks driven by a mechanism. It is also the only curve that is easy to see over
90
+ a short distance. Constant speed shows in the timing, and an overshoot needs
91
+ distance to show.
86
92
 
87
93
  ## Material
88
94
 
89
- | Field | Decides |
95
+ | Field | What it controls |
90
96
  |---|---|
91
97
  | `material` | `blur` in pixels and `opacity` from 0 to 1 |
92
98
 
93
- **This is the one choice that can invalidate the contrast guarantee.** Contrast
94
- is measured against a known ground, and a translucent surface does not have one,
95
- so a pair over it comes back `unverifiable` rather than measured. That is
96
- reported rather than hidden: a theme with heavy material is making a trade and
97
- should be able to see it.
99
+ **`material` is the one field that can break the contrast guarantee.** The
100
+ engine measures contrast against a known ground. A translucent surface has no
101
+ known ground, so a pair over it is reported as `unverifiable`. The report shows
102
+ you the trade-off that a theme with heavy material makes.
98
103
 
99
104
  ## Output
100
105
 
101
- | Field | Decides |
106
+ | Field | What it controls |
102
107
  |---|---|
103
108
  | `namespace` | The token prefix. Defaults to `helix` |
104
109
 
105
- `namespace` is for a product shipping its own token layer built on this engine.
106
- Everything else in the library reads `--helix-*`, so changing it is a decision
107
- about your own surface rather than about Helix's.
110
+ Use `namespace` when your product ships its own token layer built on the
111
+ engine. The rest of the library still reads `--helix-*`, so a new namespace
112
+ changes only your own tokens.
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: What Helix is, and the four packages it stacks into.
2
+ description: What Helix is, and the packages it includes.
3
3
  order: 1
4
4
  section: Getting started
5
5
  title: Introduction
@@ -7,22 +7,25 @@ title: Introduction
7
7
 
8
8
  # Helix
9
9
 
10
- A design system of web components: tokens, a headless component tier, a styled
11
- tier, and the framework typings generated from both. It works in any framework,
12
- or none, because the components are custom elements.
10
+ Helix is a design system of web components. It includes:
13
11
 
14
- Two things make it different from a component library.
12
+ - color, space, and type tokens
13
+ - a headless tier with the structure and behavior of each component
14
+ - a styled tier that adds the visual design
15
+ - typings for each framework, generated from both tiers
15
16
 
16
- **Theming is derivation, not override.** You supply a handful of intent inputs
17
- and the system computes a complete token set whose every foreground and
18
- background pair meets its contrast target by construction. See
19
- [Theming](./theming).
17
+ The components are custom elements, so you can use them in any framework or
18
+ without one.
20
19
 
21
- **AI is a first-class consumer.** Components are described by a custom elements
22
- manifest with typed enums, documented parts, and generated React prop names. An
23
- agent can write correct Helix code by reading types alone. That is why
24
- conventions like inline literal unions and documented CSS parts are enforced at
25
- analysis time rather than left to review.
20
+ **Helix computes your theme.** You enter a few inputs, such as your brand color,
21
+ and Helix computes every token from them. Every text and background pair meets
22
+ its contrast target. See [Theming](./theming).
23
+
24
+ **AI agents can read it.** A custom elements manifest describes every component,
25
+ with typed options, documented parts, and generated React prop names, so an
26
+ agent can write correct Helix code from the types alone. The build checks
27
+ conventions such as inline literal unions and documented CSS parts for that
28
+ reason.
26
29
 
27
30
  ## Install
28
31
 
@@ -30,7 +33,7 @@ analysis time rather than left to review.
30
33
  bun add @fusion.dev/helix @fusion.dev/helix-styles
31
34
  ```
32
35
 
33
- Two imports at your app root, in this order, then the elements are usable
36
+ Add two imports at your app root, in this order. Then you can use the elements
34
37
  anywhere:
35
38
 
36
39
  ```ts
@@ -42,13 +45,13 @@ import '@fusion.dev/helix'
42
45
  <helix-accordion separation="outlined">
43
46
  <helix-accordion-item default-open>
44
47
  <span slot="summary">What is Helix?</span>
45
- <p>A design system that derives its own accessible palette.</p>
48
+ <p>A design system of web components, themed from your brand color.</p>
46
49
  </helix-accordion-item>
47
50
  </helix-accordion>
48
51
  ```
49
52
 
50
- [Installation](./installation) has the order those imports are in and why, and a
51
- page for each framework.
53
+ [Installation](./installation) explains the import order and links to a page for
54
+ each framework.
52
55
 
53
56
  ## Packages
54
57
 
@@ -61,9 +64,8 @@ page for each framework.
61
64
  | `@fusion.dev/helix-icons` | You are drawing an icon outside a component |
62
65
  | `@fusion.dev/helix-core` | You are building a new Helix component |
63
66
 
64
- **There is no React package.** The elements are custom elements and React
65
- renders them directly; what ships for React is a `jsx.d.ts` that types every tag
66
- and a runtime bridge in `@fusion.dev/helix-core` that repairs React's hydration of
67
- custom events. [React](./react) has both.
67
+ React uses the elements directly. Helix ships a `jsx.d.ts` that types every
68
+ tag. `@fusion.dev/helix-core` has a runtime bridge that fixes how React hydrates
69
+ custom events. [React](./react) covers both.
68
70
 
69
- See [Tiers](./tiers) for how the packages relate and which one to reach for.
71
+ See [Tiers](./tiers) for how the packages relate and which one to use.