@fusion.dev/helix 0.186.1 → 0.188.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/api.json +1 -0
- package/custom-elements.json +12 -4
- package/dist/orb/shapes.d.ts +2 -2
- package/dist/table/root/table-root.d.ts +1 -0
- package/examples.json +1 -0
- package/lint.json +1 -1
- package/package.json +12 -6
- package/skills/helix/SKILL.md +476 -0
- package/types/jsx.d.ts +6 -4
- package/types/vue.d.ts +1 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fusion.dev/helix",
|
|
3
3
|
"description": "Styled component tier. Extends the headless tier with token-driven presentation.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.188.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ross Sheppard <ross.sheppard@fusion.dev>",
|
|
7
7
|
"repository": {
|
|
@@ -21,21 +21,27 @@
|
|
|
21
21
|
"types": "./dist/*/index.d.ts",
|
|
22
22
|
"default": "./dist/*/index.js"
|
|
23
23
|
},
|
|
24
|
+
"./api.json": "./api.json",
|
|
24
25
|
"./custom-elements.json": "./custom-elements.json",
|
|
26
|
+
"./examples.json": "./examples.json",
|
|
25
27
|
"./jsx": "./types/jsx.d.ts",
|
|
26
28
|
"./lint.json": "./lint.json",
|
|
27
29
|
"./rehype": {
|
|
28
30
|
"types": "./dist/rehype/rehype.d.ts",
|
|
29
31
|
"default": "./dist/rehype/rehype.js"
|
|
30
32
|
},
|
|
33
|
+
"./skills/*": "./skills/*",
|
|
31
34
|
"./svelte": "./types/svelte.d.ts",
|
|
32
35
|
"./vue": "./types/vue.d.ts",
|
|
33
36
|
"./words": "./types/words.d.ts"
|
|
34
37
|
},
|
|
35
38
|
"files": [
|
|
39
|
+
"api.json",
|
|
36
40
|
"custom-elements.json",
|
|
37
41
|
"dist",
|
|
42
|
+
"examples.json",
|
|
38
43
|
"lint.json",
|
|
44
|
+
"skills",
|
|
39
45
|
"types"
|
|
40
46
|
],
|
|
41
47
|
"customElements": "custom-elements.json",
|
|
@@ -54,14 +60,14 @@
|
|
|
54
60
|
},
|
|
55
61
|
"bugs": "https://github.com/project-fusion/fusion/issues",
|
|
56
62
|
"dependencies": {
|
|
57
|
-
"@fusion.dev/helix-core": "0.
|
|
58
|
-
"@fusion.dev/helix-headless": "0.
|
|
59
|
-
"@fusion.dev/helix-styles": "0.
|
|
63
|
+
"@fusion.dev/helix-core": "0.188.0",
|
|
64
|
+
"@fusion.dev/helix-headless": "0.188.0",
|
|
65
|
+
"@fusion.dev/helix-styles": "0.188.0",
|
|
60
66
|
"tslib": "^2.8.1"
|
|
61
67
|
},
|
|
62
68
|
"devDependencies": {
|
|
63
|
-
"@fusion.dev/helix-cem": "0.
|
|
64
|
-
"@fusion.dev/helix-testing": "0.
|
|
69
|
+
"@fusion.dev/helix-cem": "0.188.0",
|
|
70
|
+
"@fusion.dev/helix-testing": "0.188.0",
|
|
65
71
|
"@fusion.dev/scripts": "0.0.0-dev",
|
|
66
72
|
"@happy-dom/global-registrator": "^20.14.5",
|
|
67
73
|
"@lit-labs/ssr": "^4.1.0",
|
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: helix
|
|
3
|
+
description: Writes and checks pages built with Helix, the web components in @fusion.dev/helix. Use when writing or reviewing code with helix- elements, Helix tokens, or the helix command line.
|
|
4
|
+
metadata:
|
|
5
|
+
version: "0.188.0"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Helix
|
|
9
|
+
|
|
10
|
+
This guide is for @fusion.dev/helix 0.188.0, the version installed beside it.
|
|
11
|
+
|
|
12
|
+
## Using Helix with an agent
|
|
13
|
+
|
|
14
|
+
This guide is in two places, and both say the same thing:
|
|
15
|
+
|
|
16
|
+
- this page, which [llms.txt](https://fusion.dev/llms.txt) indexes
|
|
17
|
+
- `skills/helix/SKILL.md` inside `@fusion.dev/helix`, which matches the version
|
|
18
|
+
you installed and works offline
|
|
19
|
+
|
|
20
|
+
[Link your tool to this guide](#link-your-tool-to-this-guide) says how to point
|
|
21
|
+
each tool at one of them. [The MCP server](https://fusion.dev/helix/docs/coding-agents) works with any
|
|
22
|
+
vendor and runs code, and Claude, Cursor, Windsurf, and Zed all support it.
|
|
23
|
+
|
|
24
|
+
### Read the family before writing its tags
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
https://fusion.dev/helix/components/<family>.md
|
|
28
|
+
https://fusion.dev/helix/components/<family>.json
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Each family's file lists:
|
|
32
|
+
|
|
33
|
+
- the import lines for the family
|
|
34
|
+
- every tag
|
|
35
|
+
- every property, with its literal union
|
|
36
|
+
- every event, with its class and its React prop
|
|
37
|
+
- every slot, part, and custom property
|
|
38
|
+
- the examples, each with the import lines it needs
|
|
39
|
+
|
|
40
|
+
**If a tag is not in a family's file, it does not exist.** A property value
|
|
41
|
+
outside a declared union is invalid. Property types are literal unions for this
|
|
42
|
+
reason. `'ghost' | 'outlined' | 'solid' | 'subtle'` shows you the values, and a
|
|
43
|
+
named type such as `ButtonEmphasis` sends you somewhere else to find them.
|
|
44
|
+
|
|
45
|
+
The manifest at `https://fusion.dev/helix/custom-elements.json` holds every
|
|
46
|
+
family in one file, which is several megabytes. Read the families you use.
|
|
47
|
+
Start from `https://fusion.dev/llms.txt`. In one request, it names every
|
|
48
|
+
document and the machine-readable sources.
|
|
49
|
+
|
|
50
|
+
### Five mistakes that look like Helix bugs
|
|
51
|
+
|
|
52
|
+
Each of these looks like a bug in Helix. They are listed from the most common to
|
|
53
|
+
the least common.
|
|
54
|
+
|
|
55
|
+
#### 1. Writing a tag without importing its family
|
|
56
|
+
|
|
57
|
+
Each tag needs its family's entry imported. A family does not register another
|
|
58
|
+
family's elements that you write inside it, so a Select needs Option imported
|
|
59
|
+
beside it, and a Data Table needs Table:
|
|
60
|
+
|
|
61
|
+
```js
|
|
62
|
+
import '@fusion.dev/helix/select'
|
|
63
|
+
import '@fusion.dev/helix/option'
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Without the second line, the options are unknown elements and the Select offers
|
|
67
|
+
nothing. Each family's Usage block and each example list the lines to import.
|
|
68
|
+
Copy them with the markup.
|
|
69
|
+
|
|
70
|
+
#### 2. Blocking a change without `preventDefault()`
|
|
71
|
+
|
|
72
|
+
**Setting `value`, `open`, `checked`, or `active` sets the starting state and
|
|
73
|
+
corrects the current state.** The element starts in that state and then applies
|
|
74
|
+
its own changes, the same as `<input value="Ada">`. This works with no handler:
|
|
75
|
+
|
|
76
|
+
```html
|
|
77
|
+
<helix-input
|
|
78
|
+
label="Name"
|
|
79
|
+
value="Ada"
|
|
80
|
+
></helix-input>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The mistake happens when you block a change. React restores a native `<input>`
|
|
84
|
+
whose state did not change. React does not do this for a custom element, so a
|
|
85
|
+
handler that does not call `setState` leaves the element with the new value. To
|
|
86
|
+
block a change, call `preventDefault()` on the matching `helix-before-*` event:
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
<helix-input
|
|
90
|
+
label="Name"
|
|
91
|
+
onhelix-before-change={(event) => {
|
|
92
|
+
event.preventDefault()
|
|
93
|
+
setName(clean(event.detail.value))
|
|
94
|
+
}}
|
|
95
|
+
value={name}
|
|
96
|
+
/>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
A form reset returns the element to `default-value` and the matching attributes
|
|
100
|
+
for the other states. See [State and control](https://fusion.dev/helix/docs/state-and-control).
|
|
101
|
+
|
|
102
|
+
#### 3. Using the exact event name
|
|
103
|
+
|
|
104
|
+
In React, write `onhelix-before-open`. `onHelixOpen` and `onOpen` do not work.
|
|
105
|
+
React removes `on` from the prop name and listens for exactly the rest, so the
|
|
106
|
+
casing must match. Outside React, use
|
|
107
|
+
`addEventListener('helix-before-open', …)`.
|
|
108
|
+
|
|
109
|
+
**Lifecycle events do not bubble. Coordination events do.**
|
|
110
|
+
`helix-before-open` and `helix-before-close` reach the element and stop there,
|
|
111
|
+
so a menu closing inside a dialog does not close the dialog. `helix-select`,
|
|
112
|
+
`helix-before-change`, and `helix-dismiss` cross shadow boundaries, so you can
|
|
113
|
+
listen for them on a container. If a listener on a wrapper receives nothing, the
|
|
114
|
+
event is a lifecycle event. Put the listener on the element.
|
|
115
|
+
|
|
116
|
+
#### 4. Styling a disabled control with `:disabled`
|
|
117
|
+
|
|
118
|
+
```css
|
|
119
|
+
helix-input:disabled { … } /* right: matches a disabled fieldset too */
|
|
120
|
+
helix-input[disabled] { … } /* wrong: a fieldset sets no attribute */
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
The browser disables a control inside `<fieldset disabled>` without adding an
|
|
124
|
+
attribute to it. The `[disabled]` selector does not match it, so the control
|
|
125
|
+
looks enabled while it does not accept input.
|
|
126
|
+
|
|
127
|
+
#### 5. Loading every style layer before the elements
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
import '@fusion.dev/helix-styles/helix.css'
|
|
131
|
+
import '@fusion.dev/helix/button'
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`helix.css` combines four files in the order they must load:
|
|
135
|
+
|
|
136
|
+
1. `primitives.css`
|
|
137
|
+
2. `tokens.css`
|
|
138
|
+
3. `base.css`
|
|
139
|
+
4. `elements.css`
|
|
140
|
+
|
|
141
|
+
Import them one by one only when you need to replace one of them.
|
|
142
|
+
|
|
143
|
+
People most often leave out `elements.css`. Everything works without it until
|
|
144
|
+
you render the page on the server. An element that has not upgraded has no
|
|
145
|
+
styles of its own, so the markup appears unstyled and then jumps into place.
|
|
146
|
+
|
|
147
|
+
Each missing layer has its own symptom:
|
|
148
|
+
|
|
149
|
+
- An unstyled flash before hydration means `elements.css` is missing.
|
|
150
|
+
- Unstyled headings mean `base.css` is missing.
|
|
151
|
+
- No color anywhere means `primitives.css` or `tokens.css` is missing.
|
|
152
|
+
|
|
153
|
+
To tell the last two apart, look at the stylesheet. The screen looks the same.
|
|
154
|
+
Without `tokens.css`, nothing declares `--helix-color-*`. Without
|
|
155
|
+
`primitives.css`, all 185 semantic tokens are declared and every color resolves
|
|
156
|
+
to `rgba(0, 0, 0, 0)`, because `tokens.css` refers to a primitive 297 times and
|
|
157
|
+
to nothing else. A missing `primitives.css` looks like a working install, and
|
|
158
|
+
`helix.css` exists to prevent it.
|
|
159
|
+
|
|
160
|
+
### Choosing a component family
|
|
161
|
+
|
|
162
|
+
Look up the family in the manifest. Each family's `helix.aliases` lists its
|
|
163
|
+
other names, and a request often uses one of them:
|
|
164
|
+
|
|
165
|
+
- `Segment` and `NavItem` are `Toggle`.
|
|
166
|
+
- `Key` is `Legend`.
|
|
167
|
+
- `Hint` is `Tooltip`.
|
|
168
|
+
|
|
169
|
+
Know two rules before you search:
|
|
170
|
+
|
|
171
|
+
- **A group that reports a form value is a part.** A group that does not is its
|
|
172
|
+
own family. `Radio.Group` and `Toggle.Group` are parts. `Checkbox Group` and
|
|
173
|
+
`Button Group` are families whose members each submit their own value.
|
|
174
|
+
- **A control has no `size`.** Its height comes from `data-density` on the
|
|
175
|
+
region, `sm` / `md` / `lg`, which crosses shadow boundaries through custom
|
|
176
|
+
properties. If two controls in a row need different heights, change the
|
|
177
|
+
layout.
|
|
178
|
+
|
|
179
|
+
### Styling components from outside
|
|
180
|
+
|
|
181
|
+
You can style a component in three ways. Try them in this order:
|
|
182
|
+
|
|
183
|
+
1. **A token.** `--helix-seed-radii: 2px` at `:root` reaches every component,
|
|
184
|
+
because custom properties inherit. Most requests to restyle the button are
|
|
185
|
+
really requests for less rounding across the brand. Set the seed. The four
|
|
186
|
+
radii, including `--helix-radii-md`, are computed from it, so changing `md`
|
|
187
|
+
alone changes neither the buttons nor the cards.
|
|
188
|
+
2. **A per-component hook.** For example, `--card-padding` on `helix-card`. Each
|
|
189
|
+
hook is named after its component, documented on the component's page, and
|
|
190
|
+
defaults to a semantic token.
|
|
191
|
+
3. **A part.** For example, `helix-select::part(trigger)`. A part gives you the
|
|
192
|
+
most control, and it ties your styles to the component's internal structure.
|
|
193
|
+
|
|
194
|
+
**Never read a primitive.** `--_helix-neutral-500` and the other primitives
|
|
195
|
+
belong to `@fusion.dev/helix-styles`. The underscore marks them as internal.
|
|
196
|
+
Read the semantic tokens that refer to them.
|
|
197
|
+
|
|
198
|
+
### Styling HTML without custom elements
|
|
199
|
+
|
|
200
|
+
`@fusion.dev/helix-styles` is plain CSS with no JavaScript. It includes recipes
|
|
201
|
+
for markup that Helix does not render:
|
|
202
|
+
|
|
203
|
+
```html
|
|
204
|
+
<button
|
|
205
|
+
data-button="solid"
|
|
206
|
+
data-button-tone="primary"
|
|
207
|
+
>Save</button>
|
|
208
|
+
<article data-prose><h2>From a CMS</h2></article>
|
|
209
|
+
<div data-density="sm">…</div>
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
A recipe sets appearance only. It has none of these:
|
|
213
|
+
|
|
214
|
+
- state
|
|
215
|
+
- form participation
|
|
216
|
+
- focus trapping
|
|
217
|
+
- keyboard handling
|
|
218
|
+
|
|
219
|
+
If you are adding a script to a recipe, use the component.
|
|
220
|
+
|
|
221
|
+
### Link your tool to this guide
|
|
222
|
+
|
|
223
|
+
**Point your tool at the guide, and do not copy it.** A copy starts to go stale
|
|
224
|
+
as soon as you paste it, and the guide changes with the library.
|
|
225
|
+
|
|
226
|
+
The skill in the package matches the Helix you installed. In Claude Code, link
|
|
227
|
+
it into your project's skills folder:
|
|
228
|
+
|
|
229
|
+
```sh
|
|
230
|
+
mkdir -p .claude/skills
|
|
231
|
+
ln -s ../../node_modules/@fusion.dev/helix/skills/helix .claude/skills/helix
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Other tools read a file in your repository. Add a line to it that names the
|
|
235
|
+
skill, or this page:
|
|
236
|
+
|
|
237
|
+
```md
|
|
238
|
+
Helix: read node_modules/@fusion.dev/helix/skills/helix/SKILL.md before
|
|
239
|
+
writing components, and node_modules/@fusion.dev/helix/api.json for the API.
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
| Tool | Where it goes |
|
|
243
|
+
|---|---|
|
|
244
|
+
| Claude Code | `.claude/skills/helix`, or a line in `AGENTS.md` or `CLAUDE.md` |
|
|
245
|
+
| Cursor | `.cursor/rules/` |
|
|
246
|
+
| GitHub Copilot | `.github/copilot-instructions.md` |
|
|
247
|
+
| Anything else | Whatever it reads. The line is the same |
|
|
248
|
+
|
|
249
|
+
We do not write those files for you, and we chose this on purpose. You would
|
|
250
|
+
have to maintain a file we generated in your repository against our changes. A
|
|
251
|
+
tool that rewrote it on install would be a dependency you did not ask for, in a
|
|
252
|
+
directory you own. You add the link or the line, and you can delete it.
|
|
253
|
+
|
|
254
|
+
### Install the Helix MCP server
|
|
255
|
+
|
|
256
|
+
```sh
|
|
257
|
+
helix mcp init --client claude
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
`helix mcp` answers from the Helix packages your project installs: families,
|
|
261
|
+
examples, import lines, tokens, and the theme a brand color produces. `--client`
|
|
262
|
+
takes `claude`, `cursor`, or `vscode`, and writes the server into the file that
|
|
263
|
+
client reads. [Coding agents](https://fusion.dev/helix/docs/coding-agents) lists the tools. It runs on your
|
|
264
|
+
machine, talks to a client you chose, and makes no network requests.
|
|
265
|
+
|
|
266
|
+
### What this guide does not cover
|
|
267
|
+
|
|
268
|
+
**Anything in the manifest.** Property names, unions, part names, event names,
|
|
269
|
+
and hooks change with the library, and they are in a file you can fetch. A list
|
|
270
|
+
of them here would be a second copy that goes stale. Fetch the family.
|
|
271
|
+
|
|
272
|
+
**The theme a brand color produces.** Helix computes it, so call
|
|
273
|
+
`helix_derive_theme`, or run `helix theme build`. Both take the same JSON that a
|
|
274
|
+
`helix.theme.json` file holds.
|
|
275
|
+
|
|
276
|
+
**Whether a component fits your case.** Read the examples to decide. Every
|
|
277
|
+
family's `.md` file includes them as text, and
|
|
278
|
+
`https://fusion.dev/helix/sandbox?e=<family>.<slug>` opens one for editing.
|
|
279
|
+
|
|
280
|
+
## Coding agents
|
|
281
|
+
|
|
282
|
+
We publish Helix as text for agents that write Helix code, so an agent does not
|
|
283
|
+
need to read the rendered website. Three sources give it the library as text.
|
|
284
|
+
We generate all three from the same sources as this site.
|
|
285
|
+
|
|
286
|
+
### The manifest
|
|
287
|
+
|
|
288
|
+
```
|
|
289
|
+
https://fusion.dev/helix/custom-elements.json
|
|
290
|
+
@fusion.dev/helix/custom-elements.json
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
The URL and the package path serve the same file. A package path works only
|
|
294
|
+
from a `node_modules` folder. An agent that found Helix through `llms.txt` can
|
|
295
|
+
reach this site and has no checkout, so it needs the URL.
|
|
296
|
+
|
|
297
|
+
The manifest lists:
|
|
298
|
+
|
|
299
|
+
- every element
|
|
300
|
+
- every property, with its literal type union
|
|
301
|
+
- every event, with its class
|
|
302
|
+
- every slot
|
|
303
|
+
- every part
|
|
304
|
+
- every custom property
|
|
305
|
+
|
|
306
|
+
We generate the framework typings, the reference documentation, and the site's
|
|
307
|
+
component pages from the manifest, so they always match it.
|
|
308
|
+
|
|
309
|
+
**If a tag is not in the manifest, it does not exist.** An agent can check every
|
|
310
|
+
tag and value it writes against the file, so an invented component is caught. A
|
|
311
|
+
prop value outside a declared union is invalid.
|
|
312
|
+
|
|
313
|
+
For the same reason, the analyzer requires literal unions for property types.
|
|
314
|
+
`'ghost' | 'outlined' | 'solid' | 'subtle'` shows you what to write. A named
|
|
315
|
+
type such as `ButtonEmphasis` sends you somewhere else to look.
|
|
316
|
+
|
|
317
|
+
### Markdown and JSON for each component family
|
|
318
|
+
|
|
319
|
+
```
|
|
320
|
+
https://fusion.dev/helix/components/<family>.md
|
|
321
|
+
https://fusion.dev/helix/components/<family>.json
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
These are static files, one pair for each family. Read the family you are
|
|
325
|
+
writing instead of the whole manifest. The manifest holds every family in one
|
|
326
|
+
file, and it is several megabytes.
|
|
327
|
+
|
|
328
|
+
The `.md` file has:
|
|
329
|
+
|
|
330
|
+
- the family's metadata
|
|
331
|
+
- a Usage block with the import lines for the family
|
|
332
|
+
- every element's description and API tables
|
|
333
|
+
- the examples, each with the import lines it needs
|
|
334
|
+
|
|
335
|
+
The HTML page leaves the examples out on purpose. The page lists what a
|
|
336
|
+
component can do, and the sandbox beside it shows what the component looks
|
|
337
|
+
like. Keeping pictures out of the reference keeps it the one place you can trust
|
|
338
|
+
to be complete. An agent cannot use the sandbox, and it reads an example as
|
|
339
|
+
text, so the `.md` file includes the examples.
|
|
340
|
+
|
|
341
|
+
Each example the visual check pictures links its pictures under it: wide at
|
|
342
|
+
640 pixels, narrow at 390, and dark at 640, taken in the environment that
|
|
343
|
+
`examples.json` records. Compare a render of your own to them.
|
|
344
|
+
|
|
345
|
+
The `.json` file has the same family as structured data, and three fields an
|
|
346
|
+
agent uses most:
|
|
347
|
+
|
|
348
|
+
- `usage`, the import block for each tier
|
|
349
|
+
- `imports` on each example, an import line for every element it writes
|
|
350
|
+
- `specifier` and `words` on each element: the line that registers it, and the
|
|
351
|
+
properties `registerWords` translates
|
|
352
|
+
|
|
353
|
+
### Import every family you write
|
|
354
|
+
|
|
355
|
+
Each tag needs its family's entry imported. A family registers its own parts,
|
|
356
|
+
so `@fusion.dev/helix/tabs` registers Tab and Tab Panel. A family does not
|
|
357
|
+
register another family's elements that you write inside it:
|
|
358
|
+
|
|
359
|
+
```js
|
|
360
|
+
import '@fusion.dev/helix/select'
|
|
361
|
+
import '@fusion.dev/helix/option'
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
Without the Option import, the options are unknown elements and the Select
|
|
365
|
+
offers nothing. The same is true
|
|
366
|
+
of the layout around an example: a Button inside a Flex needs both imports.
|
|
367
|
+
Copy the import lines above an example together with its markup.
|
|
368
|
+
|
|
369
|
+
### Check the code you wrote
|
|
370
|
+
|
|
371
|
+
These checks run on your project, and each one prints JSON with `--json`:
|
|
372
|
+
|
|
373
|
+
| Command | What it finds |
|
|
374
|
+
|---|---|
|
|
375
|
+
| `helix scan` | Unknown tags, attributes, values, slots, parts, hooks, React event props, and tokens, a `--_helix-` primitive read, `[disabled]` on a form control, a selector on an attribute the element never writes back, and a tag whose entry nothing imports, each with its file and line |
|
|
376
|
+
| `helix theme audit` | Every foreground and background pair in your theme that misses its contrast target |
|
|
377
|
+
| `initDiagnostics()` from `@fusion.dev/helix-diagnostics` | The same findings on the running page, and what the library reports as it runs, such as an option nobody imported |
|
|
378
|
+
|
|
379
|
+
`helix scan` and `helix theme audit` exit 1 when they find an error and 0 when
|
|
380
|
+
they find none. Run them after you write Helix code, and fix each finding
|
|
381
|
+
before you finish. `helix scan --file <path>` reports one file's findings, and
|
|
382
|
+
`helix scan --stdin tsx` reads a snippet from stdin. Both still read the whole
|
|
383
|
+
project, so an import in another file counts.
|
|
384
|
+
|
|
385
|
+
### The index
|
|
386
|
+
|
|
387
|
+
```
|
|
388
|
+
/llms.txt
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
`llms.txt` lists every page on the site, in groups. Each entry links to the
|
|
392
|
+
page's `.md` file and has a one-sentence summary. We generate it from the same
|
|
393
|
+
route registry as the sitemap. Every page appears in it, and every entry in it
|
|
394
|
+
is a page that exists.
|
|
395
|
+
|
|
396
|
+
Start with `llms.txt`. In one request, it names every page on the site and the
|
|
397
|
+
two machine-readable sources above. This guide gives no page count, because a
|
|
398
|
+
number typed into prose goes stale.
|
|
399
|
+
|
|
400
|
+
### Links on each component page
|
|
401
|
+
|
|
402
|
+
Each family page has a menu beside its title with:
|
|
403
|
+
|
|
404
|
+
- **View Markdown**
|
|
405
|
+
- **Copy Markdown**
|
|
406
|
+
- links that send the document's URL to Claude or to Cursor
|
|
407
|
+
|
|
408
|
+
The links send the URL, because the URL stays current. A pasted copy starts to
|
|
409
|
+
go stale as soon as you paste it.
|
|
410
|
+
|
|
411
|
+
### The Helix MCP server
|
|
412
|
+
|
|
413
|
+
```sh
|
|
414
|
+
helix mcp init --client claude
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
`helix mcp` runs an MCP server over stdio. It answers from the Helix packages
|
|
418
|
+
your project installs, so every answer is for the version you build with, and
|
|
419
|
+
each one names that package and version. `helix mcp init` adds the server to
|
|
420
|
+
the file your client reads and keeps every other server in it:
|
|
421
|
+
|
|
422
|
+
| Client | File |
|
|
423
|
+
|---|---|
|
|
424
|
+
| `claude` | `.mcp.json` |
|
|
425
|
+
| `cursor` | `.cursor/mcp.json` |
|
|
426
|
+
| `vscode` | `.vscode/mcp.json` |
|
|
427
|
+
|
|
428
|
+
The server runs `npx @fusion.dev/helix-cli mcp`, which is the copy in your
|
|
429
|
+
project. It has nine tools:
|
|
430
|
+
|
|
431
|
+
| Tool | What it returns |
|
|
432
|
+
|---|---|
|
|
433
|
+
| `helix_find` | The families that answer a name, an alias, a tag, or a purpose, best first |
|
|
434
|
+
| `helix_family` | One family's tags, properties and their unions, events and their React props, slots, parts, custom properties, words, and import lines |
|
|
435
|
+
| `helix_example` | One example as HTML and TSX, with its stylesheet, the import lines it needs, and links to its pictures |
|
|
436
|
+
| `helix_imports` | The import lines for a list of tags, and the closest tag to any unknown one |
|
|
437
|
+
| `helix_tokens` | The semantic tokens, by a word in the name or description and by category |
|
|
438
|
+
| `helix_check` | The findings `helix scan` reports for the files you name, or for a snippet before you write it |
|
|
439
|
+
| `helix_check_page` | The findings the page diagnostics report for each address you name, opened headless, with what the library logged as the page ran |
|
|
440
|
+
| `helix_audit_theme` | Every foreground and background pair a brand color derives to, with its ratio and target |
|
|
441
|
+
| `helix_derive_theme` | The theme's CSS |
|
|
442
|
+
|
|
443
|
+
Each result is structured, with an output schema, and the same JSON as text.
|
|
444
|
+
A name that matches more than one family, such as `Dropdown`, returns an error
|
|
445
|
+
listing each family. The theme tools take the same JSON that a
|
|
446
|
+
`helix.theme.json` file holds.
|
|
447
|
+
|
|
448
|
+
Each lookup is also a command, for a script or a person:
|
|
449
|
+
|
|
450
|
+
```sh
|
|
451
|
+
helix api find dropdown
|
|
452
|
+
helix api family select
|
|
453
|
+
helix api example select
|
|
454
|
+
helix api imports helix-select helix-option
|
|
455
|
+
helix api tokens surface --category color
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
`--json` prints what the MCP tool returns.
|
|
459
|
+
|
|
460
|
+
**`helix_derive_theme` does not return a palette that fails its own targets**,
|
|
461
|
+
the same as `helix theme build`. A failing palette pasted into a stylesheet is
|
|
462
|
+
worse than a warning above it, and an agent does not read warnings.
|
|
463
|
+
|
|
464
|
+
You run `helix mcp` yourself, and we do not host it. A server we operated would
|
|
465
|
+
be one more service between you and your own tokens. `helix mcp` is a
|
|
466
|
+
subcommand of a binary you installed. It talks to a client you chose and makes
|
|
467
|
+
no network requests.
|
|
468
|
+
|
|
469
|
+
### What we do not publish for agents
|
|
470
|
+
|
|
471
|
+
**`llms-full.txt`, or any other whole-library download of the pages.** A single
|
|
472
|
+
file with every page goes out of date as soon as anything changes. Each link in
|
|
473
|
+
`llms.txt` can be cached on its own and stays current on its own.
|
|
474
|
+
|
|
475
|
+
`llms.txt` is the index, in one request, and each family's `.md` file is its
|
|
476
|
+
document. Fetch the families you use, or ask `helix mcp`.
|
package/types/jsx.d.ts
CHANGED
|
@@ -19495,7 +19495,7 @@ The state is `aria-pressed`, not this. */
|
|
|
19495
19495
|
validation.
|
|
19496
19496
|
|
|
19497
19497
|
**Not reflected.** Reflection exists so a selector can reach a value, and
|
|
19498
|
-
no stylesheet
|
|
19498
|
+
no Helix stylesheet selects on `type`. Reflecting it writes
|
|
19499
19499
|
`type="text"` onto every `<helix-headless-input>` ever rendered, an
|
|
19500
19500
|
attribute no consumer's markup has, which is the fault the area rules write
|
|
19501
19501
|
up under `Button`'s `href`, `target` and `rel`.
|
|
@@ -19742,7 +19742,7 @@ The state is `aria-pressed`, not this. */
|
|
|
19742
19742
|
validation.
|
|
19743
19743
|
|
|
19744
19744
|
**Not reflected.** Reflection exists so a selector can reach a value, and
|
|
19745
|
-
no stylesheet
|
|
19745
|
+
no Helix stylesheet selects on `type`. Reflecting it writes
|
|
19746
19746
|
`type="text"` onto every `<helix-headless-input>` ever rendered, an
|
|
19747
19747
|
attribute no consumer's markup has, which is the fault the area rules write
|
|
19748
19748
|
up under `Button`'s `href`, `target` and `rel`.
|
|
@@ -55460,7 +55460,7 @@ export type CustomElements = {
|
|
|
55460
55460
|
* validation.
|
|
55461
55461
|
*
|
|
55462
55462
|
* **Not reflected.** Reflection exists so a selector can reach a value, and
|
|
55463
|
-
* no stylesheet
|
|
55463
|
+
* no Helix stylesheet selects on `type`. Reflecting it writes
|
|
55464
55464
|
* `type="text"` onto every `<helix-headless-input>` ever rendered, an
|
|
55465
55465
|
* attribute no consumer's markup has, which is the fault the area rules write
|
|
55466
55466
|
* up under `Button`'s `href`, `target` and `rel`.
|
|
@@ -67822,6 +67822,7 @@ export type CustomElements = {
|
|
|
67822
67822
|
* Areas where markup can be added to the component.
|
|
67823
67823
|
*
|
|
67824
67824
|
* - `(default)`: The table's rows.
|
|
67825
|
+
* - `columns`: The columns, as `TableColumn` elements. The table moves each column here itself, so markup that runs in a browser leaves it out; a server-rendered page writes `slot="columns"` on each column so the headings sit in place before the table upgrades.
|
|
67825
67826
|
* - `empty`: Content shown when there are no rows and none are loading, such as a picture and a link. Falls back to `empty-text`, which the live region still says.
|
|
67826
67827
|
* - `error`: Content shown while `error-text` is set, such as a button that tries again. Falls back to `error-text`, which the live region still says.
|
|
67827
67828
|
* - `heading`: Words that name the table, shown above it in the weight of a heading.
|
|
@@ -85722,7 +85723,7 @@ export type CustomElementsSolidJs = {
|
|
|
85722
85723
|
* validation.
|
|
85723
85724
|
*
|
|
85724
85725
|
* **Not reflected.** Reflection exists so a selector can reach a value, and
|
|
85725
|
-
* no stylesheet
|
|
85726
|
+
* no Helix stylesheet selects on `type`. Reflecting it writes
|
|
85726
85727
|
* `type="text"` onto every `<helix-headless-input>` ever rendered, an
|
|
85727
85728
|
* attribute no consumer's markup has, which is the fault the area rules write
|
|
85728
85729
|
* up under `Button`'s `href`, `target` and `rel`.
|
|
@@ -98312,6 +98313,7 @@ export type CustomElementsSolidJs = {
|
|
|
98312
98313
|
* Areas where markup can be added to the component.
|
|
98313
98314
|
*
|
|
98314
98315
|
* - `(default)`: The table's rows.
|
|
98316
|
+
* - `columns`: The columns, as `TableColumn` elements. The table moves each column here itself, so markup that runs in a browser leaves it out; a server-rendered page writes `slot="columns"` on each column so the headings sit in place before the table upgrades.
|
|
98315
98317
|
* - `empty`: Content shown when there are no rows and none are loading, such as a picture and a link. Falls back to `empty-text`, which the live region still says.
|
|
98316
98318
|
* - `error`: Content shown while `error-text` is set, such as a button that tries again. Falls back to `error-text`, which the live region still says.
|
|
98317
98319
|
* - `heading`: Words that name the table, shown above it in the weight of a heading.
|
package/types/vue.d.ts
CHANGED
|
@@ -10080,7 +10080,7 @@ The state is `aria-pressed`, not this. */
|
|
|
10080
10080
|
validation.
|
|
10081
10081
|
|
|
10082
10082
|
**Not reflected.** Reflection exists so a selector can reach a value, and
|
|
10083
|
-
no stylesheet
|
|
10083
|
+
no Helix stylesheet selects on `type`. Reflecting it writes
|
|
10084
10084
|
`type="text"` onto every `<helix-headless-input>` ever rendered, an
|
|
10085
10085
|
attribute no consumer's markup has, which is the fault the area rules write
|
|
10086
10086
|
up under `Button`'s `href`, `target` and `rel`.
|