@fusion.dev/helix-docs 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.
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Four mistakes that look like Helix bugs, and the files an agent should read.
2
+ description: Five mistakes that look like Helix bugs, and the files an agent should read.
3
3
  parent: coding-agents
4
4
  section: Getting started
5
5
  title: Using Helix with an agent
@@ -8,58 +8,63 @@ title: Using Helix with an agent
8
8
 
9
9
  # Using Helix with an agent
10
10
 
11
- **We publish this guide as a web page so that every tool can read it.** It began
12
- as `.claude/skills/using-helix/SKILL.md`, a file format that one vendor reads in
13
- a repository. Every other vendor reads its own file:
11
+ This guide is in two places, and both say the same thing:
14
12
 
15
- - Cursor reads `.cursor/rules`.
16
- - Copilot reads `.github/copilot-instructions.md`.
13
+ - this page, which [llms.txt](https://fusion.dev/llms.txt) indexes
14
+ - `skills/helix/SKILL.md` inside `@fusion.dev/helix`, which matches the version
15
+ you installed and works offline
17
16
 
18
- If you install the published packages, you have none of these files. You have
19
- this site and no checkout of our repository.
17
+ [Link your tool to this guide](#link-your-tool-to-this-guide) says how to point
18
+ each tool at one of them. [The MCP server](./coding-agents) works with any
19
+ vendor and runs code, and Claude, Cursor, Windsurf, and Zed all support it.
20
20
 
21
- So the guide lives in one place, here, at a URL that
22
- [llms.txt](https://fusion.dev/llms.txt) indexes and that each of those files can
23
- link to. Paste the link into the file your tool reads. [The MCP
24
- server](./coding-agents) works with any vendor and runs code, and Claude,
25
- Cursor, Windsurf, and Zed all support it.
26
-
27
- ## Read the manifest before writing a tag
21
+ ## Read the family before writing its tags
28
22
 
29
23
  ```
30
- https://fusion.dev/helix/custom-elements.json
31
- @fusion.dev/helix/custom-elements.json
24
+ https://fusion.dev/helix/components/<family>.md
25
+ https://fusion.dev/helix/components/<family>.json
32
26
  ```
33
27
 
34
- The manifest lists:
28
+ Each family's file lists:
35
29
 
30
+ - the import lines for the family
36
31
  - every tag
37
32
  - every property, with its literal union
38
- - every event, with its class
33
+ - every event, with its class and its React prop
39
34
  - every slot, part, and custom property
35
+ - the examples, each with the import lines it needs
40
36
 
41
- **If a tag is not in the manifest, it does not exist.** A property value outside
42
- a declared union is invalid. Property types are literal unions for this reason.
43
- `'ghost' | 'outlined' | 'solid' | 'subtle'` shows you the values, and a named
44
- type such as `ButtonEmphasis` sends you somewhere else to find them.
45
-
46
- When you need one family, fetch it as data:
47
-
48
- ```
49
- https://fusion.dev/helix/components/<family>.json
50
- https://fusion.dev/helix/components/<family>.md
51
- ```
37
+ **If a tag is not in a family's file, it does not exist.** A property value
38
+ outside a declared union is invalid. Property types are literal unions for this
39
+ reason. `'ghost' | 'outlined' | 'solid' | 'subtle'` shows you the values, and a
40
+ named type such as `ButtonEmphasis` sends you somewhere else to find them.
52
41
 
53
- The `.md` file has the API tables and the examples. The HTML page leaves the
54
- examples out on purpose. Start from `https://fusion.dev/llms.txt`. In one
55
- request, it names every document and these two sources.
42
+ The manifest at `https://fusion.dev/helix/custom-elements.json` holds every
43
+ family in one file, which is several megabytes. Read the families you use.
44
+ Start from `https://fusion.dev/llms.txt`. In one request, it names every
45
+ document and the machine-readable sources.
56
46
 
57
- ## Four mistakes that look like Helix bugs
47
+ ## Five mistakes that look like Helix bugs
58
48
 
59
49
  Each of these looks like a bug in Helix. They are listed from the most common to
60
50
  the least common.
61
51
 
62
- ### 1. Blocking a change without `preventDefault()`
52
+ ### 1. Writing a tag without importing its family
53
+
54
+ Each tag needs its family's entry imported. A family does not register another
55
+ family's elements that you write inside it, so a Select needs Option imported
56
+ beside it, and a Data Table needs Table:
57
+
58
+ ```js
59
+ import '@fusion.dev/helix/select'
60
+ import '@fusion.dev/helix/option'
61
+ ```
62
+
63
+ Without the second line, the options are unknown elements and the Select offers
64
+ nothing. Each family's Usage block and each example list the lines to import.
65
+ Copy them with the markup.
66
+
67
+ ### 2. Blocking a change without `preventDefault()`
63
68
 
64
69
  **Setting `value`, `open`, `checked`, or `active` sets the starting state and
65
70
  corrects the current state.** The element starts in that state and then applies
@@ -91,7 +96,7 @@ block a change, call `preventDefault()` on the matching `helix-before-*` event:
91
96
  A form reset returns the element to `default-value` and the matching attributes
92
97
  for the other states. See [State and control](./state-and-control).
93
98
 
94
- ### 2. Using the exact event name
99
+ ### 3. Using the exact event name
95
100
 
96
101
  In React, write `onhelix-before-open`. `onHelixOpen` and `onOpen` do not work.
97
102
  React removes `on` from the prop name and listens for exactly the rest, so the
@@ -105,7 +110,7 @@ so a menu closing inside a dialog does not close the dialog. `helix-select`,
105
110
  listen for them on a container. If a listener on a wrapper receives nothing, the
106
111
  event is a lifecycle event. Put the listener on the element.
107
112
 
108
- ### 3. Styling a disabled control with `:disabled`
113
+ ### 4. Styling a disabled control with `:disabled`
109
114
 
110
115
  ```css
111
116
  helix-input:disabled { … } /* right: matches a disabled fieldset too */
@@ -116,7 +121,7 @@ The browser disables a control inside `<fieldset disabled>` without adding an
116
121
  attribute to it. The `[disabled]` selector does not match it, so the control
117
122
  looks enabled while it does not accept input.
118
123
 
119
- ### 4. Loading every style layer before the elements
124
+ ### 5. Loading every style layer before the elements
120
125
 
121
126
  ```ts
122
127
  import '@fusion.dev/helix-styles/helix.css'
@@ -212,20 +217,28 @@ If you are adding a script to a recipe, use the component.
212
217
 
213
218
  ## Link your tool to this guide
214
219
 
215
- **Link to this page from your instructions file, and do not copy it.** Each tool
216
- that reads project instructions reads a file in your repository, and each tool
217
- reads a different one. A copy starts to go stale as soon as you paste it, and
218
- this page changes with the library.
220
+ **Point your tool at the guide, and do not copy it.** A copy starts to go stale
221
+ as soon as you paste it, and the guide changes with the library.
222
+
223
+ The skill in the package matches the Helix you installed. In Claude Code, link
224
+ it into your project's skills folder:
225
+
226
+ ```sh
227
+ mkdir -p .claude/skills
228
+ ln -s ../../node_modules/@fusion.dev/helix/skills/helix .claude/skills/helix
229
+ ```
230
+
231
+ Other tools read a file in your repository. Add a line to it that names the
232
+ skill, or this page:
219
233
 
220
234
  ```md
221
- Helix: read https://fusion.dev/helix/docs/using-helix-with-an-agent
222
- before writing components, and https://fusion.dev/helix/custom-elements.json
223
- for the API.
235
+ Helix: read node_modules/@fusion.dev/helix/skills/helix/SKILL.md before
236
+ writing components, and node_modules/@fusion.dev/helix/api.json for the API.
224
237
  ```
225
238
 
226
239
  | Tool | Where it goes |
227
240
  |---|---|
228
- | Claude Code | `AGENTS.md` or `CLAUDE.md`, or a skill in `.claude/skills/` |
241
+ | Claude Code | `.claude/skills/helix`, or a line in `AGENTS.md` or `CLAUDE.md` |
229
242
  | Cursor | `.cursor/rules/` |
230
243
  | GitHub Copilot | `.github/copilot-instructions.md` |
231
244
  | Anything else | Whatever it reads. The line is the same |
@@ -233,22 +246,19 @@ for the API.
233
246
  We do not write those files for you, and we chose this on purpose. You would
234
247
  have to maintain a file we generated in your repository against our changes. A
235
248
  tool that rewrote it on install would be a dependency you did not ask for, in a
236
- directory you own. You paste the line above, and you can delete it.
249
+ directory you own. You add the link or the line, and you can delete it.
237
250
 
238
251
  ## Install the Helix MCP server
239
252
 
240
- ```json
241
- {
242
- "mcpServers": {
243
- "helix": { "command": "helix", "args": ["mcp"] }
244
- }
245
- }
253
+ ```sh
254
+ helix mcp init --client claude
246
255
  ```
247
256
 
248
- `helix mcp` is a subcommand of the CLI. It serves MCP over stdio, which Claude,
249
- Cursor, Windsurf, and Zed all read. It computes the theme a given brand color
250
- produces, which no document can list. It runs on your machine, talks to a client
251
- you chose, and makes no network requests.
257
+ `helix mcp` answers from the Helix packages your project installs: families,
258
+ examples, import lines, tokens, and the theme a brand color produces. `--client`
259
+ takes `claude`, `cursor`, or `vscode`, and writes the server into the file that
260
+ client reads. [Coding agents](./coding-agents) lists the tools. It runs on your
261
+ machine, talks to a client you chose, and makes no network requests.
252
262
 
253
263
  ## What this guide does not cover
254
264
 
@@ -256,10 +266,9 @@ you chose, and makes no network requests.
256
266
  and hooks change with the library, and they are in a file you can fetch. A list
257
267
  of them here would be a second copy that goes stale. Fetch the family.
258
268
 
259
- **The theme a brand color produces.** Helix computes it, so use `helix mcp`, an
260
- MCP server over stdio with `helix_audit_theme` and `helix_derive_theme`. Both
261
- take the same JSON that a `helix.theme.json` file holds. It runs on your
262
- machine, and we do not host it.
269
+ **The theme a brand color produces.** Helix computes it, so call
270
+ `helix_derive_theme`, or run `helix theme build`. Both take the same JSON that a
271
+ `helix.theme.json` file holds.
263
272
 
264
273
  **Whether a component fits your case.** Read the examples to decide. Every
265
274
  family's `.md` file includes them as text, and
package/dist/index.d.ts CHANGED
@@ -1,7 +1,6 @@
1
- export type { ApiMember, Element, EventDoc, Example, ExampleSource, Family, PropertyDoc, } from './catalog.js';
2
- export { catalog, findInCatalog } from './catalog.js';
3
- export { displayName, startCase, tierOf } from './naming.js';
4
- export { place, type ReferenceFamily, type ReferenceOptions, reference, referenceFamilies, } from './reference.js';
1
+ export type { ApiMember, Element, EventDoc, Example, ExampleSource, Family, PropertyDoc, } from '@fusion.dev/helix-cem';
2
+ export { catalog, displayName, findInCatalog, place, startCase, tierOf, } from '@fusion.dev/helix-cem';
3
+ export { type ReferenceFamily, type ReferenceOptions, reference, referenceFamilies, } from './reference.js';
5
4
  export { contentPath, type DocNavPage, type DocNavSection, type DocPage, type DocSection, findPage, navigation, pages, sections, } from './registry.js';
6
5
  export { findSection } from './sections.js';
7
6
  export { docSources } from './sources.generated.js';
package/dist/index.js CHANGED
@@ -1,6 +1,5 @@
1
- export { catalog, findInCatalog } from './catalog.js';
2
- export { displayName, startCase, tierOf } from './naming.js';
3
- export { place, reference, referenceFamilies, } from './reference.js';
1
+ export { catalog, displayName, findInCatalog, place, startCase, tierOf, } from '@fusion.dev/helix-cem';
2
+ export { reference, referenceFamilies, } from './reference.js';
4
3
  export { contentPath, findPage, navigation, pages, sections, } from './registry.js';
5
4
  export { findSection } from './sections.js';
6
5
  export { docSources } from './sources.generated.js';
@@ -258,7 +258,7 @@ export const docPages = [
258
258
  title: 'Typography',
259
259
  },
260
260
  {
261
- description: 'Four mistakes that look like Helix bugs, and the files an agent should read.',
261
+ description: 'Five mistakes that look like Helix bugs, and the files an agent should read.',
262
262
  parent: 'coding-agents',
263
263
  section: 'Getting started',
264
264
  slug: 'using-helix-with-an-agent',
@@ -32,7 +32,14 @@ export interface ReferenceExample {
32
32
  css: string;
33
33
  description: string;
34
34
  html: string;
35
+ /** The import lines that register what the example writes, printed above it. */
36
+ imports?: string[];
35
37
  label: string;
38
+ /** The baseline pictures, linked under the example beside the family's `.md`. */
39
+ renders?: {
40
+ file: string;
41
+ shot: string;
42
+ }[];
36
43
  }
37
44
  /** What to put above the reference, for a caller that generates a file. */
38
45
  export interface ReferenceOptions {
@@ -54,8 +61,8 @@ export interface ReferenceOptions {
54
61
  * Frontmatter, emitted above the heading.
55
62
  *
56
63
  * Provenance rather than decoration: a generated document should say what it
57
- * was generated from, so a reader who finds one detached from this repository
58
- * can still tell where to look. Keys are written in the order given, because
64
+ * was generated from, so a reader who finds one on its own can still tell
65
+ * where to look. Keys are written in the order given, because
59
66
  * a reader scans the first line and `title` is what they want there.
60
67
  */
61
68
  frontmatter?: Array<[string, string]>;
@@ -63,18 +70,12 @@ export interface ReferenceOptions {
63
70
  lede?: string[];
64
71
  /** Top-level heading. */
65
72
  title?: string;
73
+ /**
74
+ * The import block for each family, keyed by family slug, printed under a
75
+ * Usage heading before the first element. The catalog's `usage` is it.
76
+ */
77
+ usage?: Record<string, string[]>;
66
78
  }
67
- /**
68
- * The family and part a declaration belongs to, from where its file is.
69
- *
70
- * Not from the tag name. A tag is a string the family prefix can only be
71
- * guessed out of: `helix-button-group` is one family and `helix-tab` is a
72
- * part of another, and nothing in the name says which. The path says both.
73
- */
74
- export declare const place: (path: string | undefined) => {
75
- family: string;
76
- part: string;
77
- };
78
79
  /** One family's declarations, in the order a reference prints them. */
79
80
  export interface ReferenceFamily {
80
81
  family: string;
package/dist/reference.js CHANGED
@@ -1,4 +1,4 @@
1
- import { startCase } from './naming.js';
1
+ import { place, startCase } from '@fusion.dev/helix-cem';
2
2
  const INFRASTRUCTURE = new Set(['HelixElement']);
3
3
  const isPublic = (member) => member.privacy !== 'private' &&
4
4
  member.privacy !== 'protected' &&
@@ -14,10 +14,6 @@ const table = (depth, heading, columns, rows) => {
14
14
  const body = rows.map((row) => `| ${row.join(' | ')} |`).join('\n');
15
15
  return `\n${'#'.repeat(depth)} ${heading}\n\n${head}\n${rule}\n${body}\n`;
16
16
  };
17
- export const place = (path) => {
18
- const [, family = '', part = ''] = /^src\/([^/]+)\/([^/]+)\//.exec(path ?? '') ?? [];
19
- return { family, part };
20
- };
21
17
  export const referenceFamilies = (manifest) => {
22
18
  const families = new Map();
23
19
  for (const module of manifest.modules ?? []) {
@@ -79,7 +75,7 @@ const element = (depth, declaration, part) => {
79
75
  return lines;
80
76
  };
81
77
  const withoutSummary = (html) => html.replace(/^<!--\s*@summary[\s\S]*?-->\n*/, '');
82
- const shown = (depth, examples) => {
78
+ const shown = (depth, family, examples) => {
83
79
  if (examples.length === 0) {
84
80
  return [];
85
81
  }
@@ -89,10 +85,16 @@ const shown = (depth, examples) => {
89
85
  if (example.description) {
90
86
  lines.push(example.description, '');
91
87
  }
88
+ if (example.imports && example.imports.length > 0) {
89
+ lines.push('```js', ...example.imports, '```', '');
90
+ }
92
91
  lines.push('```html', withoutSummary(example.html), '```', '');
93
92
  if (example.css) {
94
93
  lines.push('```css', example.css, '```', '');
95
94
  }
95
+ if (example.renders && example.renders.length > 0) {
96
+ lines.push(`Pictured: ${example.renders.map((render) => `[${render.shot}](${family}/${render.file})`).join(', ')}.`, '');
97
+ }
96
98
  }
97
99
  return lines;
98
100
  };
@@ -113,10 +115,14 @@ export const reference = (manifest, options = {}) => {
113
115
  if (families.length > 1) {
114
116
  lines.push(`## ${startCase(family)}`, '');
115
117
  }
118
+ const usage = options.usage?.[family] ?? [];
119
+ if (usage.length > 0) {
120
+ lines.push(`${'#'.repeat(depth)} Usage`, '', '```js', ...usage, '```', '');
121
+ }
116
122
  for (const { declaration, part } of parts) {
117
123
  lines.push(...element(depth, declaration, part));
118
124
  }
119
- lines.push(...shown(depth, options.examples?.[family] ?? []));
125
+ lines.push(...shown(depth, family, options.examples?.[family] ?? []));
120
126
  }
121
127
  return `${lines
122
128
  .join('\n')