HelixOverviewComponentsMapsGraphIntroductionInstallationDeriving a themeSandboxTheme builderVisual testingCortexDocsEngramBuildingTraceBuildingRecallDeclaredServicesInsightsGet in touchSource on GitHub
Getting started
Using Helix with an agentThis guide is what an agent reads before writing Helix code. It is also the
helix skill: skills/helix/SKILL.md in @fusion.dev/helix, which matches the
version installed, and the skill in Helix's plugin for coding agents. Coding
agents says how to install the plugin or link the skill.Use a Helix family wherever one existsWrite a Helix element for everything Helix has a family for, and a native
element only for page structure. Before you write a native element, ask whether
a family stands in for it:helix api find <tag>, or the MCP tool helix_find, answers a native tag
with its family: button finds Button, and h1 finds Text.Each family's file lists the native elements it replaces under replaces.These native elements have families:controls: button, input of every visible type, select, and textareaoverlays and disclosure: dialog and detailstext: h1 to h6 and p, which helix-text writes with as="h1" and the
restcontent: img, picture, hr, ul, ol, table, code, pre, kbd,
blockquote, q, video, progress, and meterPage structure stays native: main, section, article, header, footer,
nav, form, label, div, span, and a. So does markup a CMS writes
inside data-prose.helix scan and the MCP tool helix_check report each native element a page
writes where an installed family stands in for it, as native-element.Look up a family before writing its tagsEach family's API is in the packages you installed, and on this site:helix api family <family>, or the MCP tool helix_family, which read every
Helix package you installednode_modules/@fusion.dev/helix/api.json, and the api.json of each other
Helix package you installed, which lists the elements it adds to a family,
such as helix-sandbox-editor in @fusion.dev/helix-sandboxhttps://fusion.dev/helix/components/<family>.md and .jsonEach lists the import lines, every tag, every property with its literal union,
every event with its React prop, every slot, part, and custom property, and the
examples with the imports each one needs.If a tag is not in a family's file, it does not exist. A property value
outside a declared union is invalid. Find a family by name, alias, or purpose
with helix api find, or the MCP tool helix_find.Five mistakes that look like Helix bugsEach of these looks like a bug in Helix. They are listed from the most common to
the least common.1. Writing a tag without importing its familyEach tag needs its family's entry imported. A family does not register another
family's elements that you write inside it, so a Select needs Option imported
beside it, and a Data Table needs Table:import'@fusion.dev/helix/select'import'@fusion.dev/helix/option'Without the second line, the options are unknown elements and the Select offers
nothing. Each family's Usage block and each example list the lines to import.
Copy them with the markup.2. Blocking a change without preventDefault()Setting value, open, checked, or active sets the starting state and
corrects the current state. The element starts in that state and then applies
its own changes, the same as <input value="Ada">. This works with no handler:<helix-inputlabel="Name"value="Ada"></helix-input>The mistake happens when you block a change. React restores a native <input>
whose state did not change. React does not do this for a custom element, so a
handler that does not call setState leaves the element with the new value. To
block a change, call preventDefault() on the matching helix-before-* event:<helix-inputlabel="Name"onhelix-before-change={(event)=>{event.preventDefault()flushSync(()=>setName(clean(event.detail.value)))}}value={name}/>Set the state inside flushSync from react-dom. Without it, React renders the
update in a later task, and keys typed faster than that are lost.A form reset returns the element to default-value and the matching attributes
for the other states. See State and control.3. Using the exact event nameIn React, write onhelix-before-open. onHelixOpen and onOpen do not work.
React removes on from the prop name and listens for exactly the rest, so the
casing must match. Outside React, use
addEventListener('helix-before-open', …).Lifecycle events do not bubble. Coordination events do.helix-before-open and helix-before-close reach the element and stop there,
so a menu closing inside a dialog does not close the dialog. helix-select,
helix-before-change, and helix-dismiss cross shadow boundaries, so you can
listen for them on a container. If a listener on a wrapper receives nothing, the
event is a lifecycle event. Put the listener on the element.4. Styling a disabled control with :disabledhelix-input:disabled{… }/* right: matches a disabled fieldset too */helix-input[disabled]{… }/* wrong: a fieldset sets no attribute */The browser disables a control inside <fieldset disabled> without adding an
attribute to it. The [disabled] selector does not match it, so the control
looks enabled while it does not accept input.5. Loading every style layer before the elementsimport'@fusion.dev/helix-styles/helix.css'import'@fusion.dev/helix/button'helix.css combines four files in the order they must load:primitives.csstokens.cssbase.csselements.cssImport them one by one only when you need to replace one of them.People most often leave out elements.css. Everything works without it until
you render the page on the server. An element that has not upgraded has no
styles of its own, so the markup appears unstyled and then jumps into place.Each missing layer has its own symptom:An unstyled flash before hydration means elements.css is missing.Unstyled headings mean base.css is missing.No color anywhere means primitives.css or tokens.css is missing.To tell the last two apart, look at the stylesheet. Without tokens.css,
nothing declares --helix-color-*. Without primitives.css, every token is
declared and every color resolves to transparent.Choosing a component familyLook up the family in the manifest. Each family's helix.aliases lists its
other names, and a request often uses one of them:Segment and NavItem are Toggle.Key is Legend.Hint is Tooltip.Know two rules before you search:A group that reports a form value is a part. A group that does not is its
own family. Radio.Group and Toggle.Group are parts. Checkbox Group and
Button Group are families whose members each submit their own value.A control has no size. Its height comes from data-density on the
region, sm / md / lg, which crosses shadow boundaries through custom
properties. If two controls in a row need different heights, change the
layout.Styling components from outsideYou can style a component in three ways. Try them in this order:A token.--helix-seed-radii: 2px at :root reaches every component,
because custom properties inherit. Most requests to restyle the button are
really requests for less rounding across the brand. Set the seed. The four
radii, including --helix-radii-md, are computed from it, so changing md
alone changes neither the buttons nor the cards.A per-component hook. For example, --card-padding on helix-card. Each
hook is named after its component, documented on the component's page, and
defaults to a semantic token.A part. For example, helix-select::part(trigger). A part gives you the
most control, and it ties your styles to the component's internal structure.Never read a primitive.--_helix-neutral-500 and the other primitives
belong to @fusion.dev/helix-styles. The underscore marks them as internal.
Read the semantic tokens that refer to them.Markup Helix does not renderHTML that a CMS or a Markdown library writes, inside an element with
data-prose, takes Helix's typography from @fusion.dev/helix-styles and needs
no Helix elements. Everywhere else, write the Helix element.Check the code you wroteRun these after you write Helix code, and fix every finding at error and
warn before you finish. Each prints JSON with --json.CommandMCP toolWhat it findshelix scanhelix_checkUnknown tags, attributes, values, slots, parts, hooks, events, and tokens, a native element a Helix family replaces, a missing import, a button inside a button, disabled="false", and the rest, each with its file and linehelix diagnostics --once <url>helix_check_pageThe same on the running page, and what the library logged as it ranhelix theme audithelix_audit_themeEvery foreground and background pair in your theme that misses its contrast targethelix scan --file <path> reports one file's findings and still reads the whole
project, so an import in another file counts. Diagnostics
lists every kind of finding, and what the checks do not cover.What this guide does not coverAnything in the manifest. Property names, unions, part names, event names,
and hooks change with the library, and they are in a file you can fetch. A list
of them here would be a second copy that goes stale. Fetch the family.The theme a brand color produces. Helix computes it, so call
helix_derive_theme, or run helix theme build. Both take the same JSON that a
helix.theme.json file holds.Whether a component fits your case. Read the examples to decide. Every
family's .md file includes them as text, and
https://fusion.dev/helix/sandbox?e=<family>.<slug> opens one for editing.