Authoring Helix content

This guide is for the **design-system manager (DSM)** — the person who owns the

design-system prose: rationale, usage guidance, gotchas, patterns, and token narratives. It explains how to edit that content now that Helix lives in one monorepo.

What changed (the cutover). Helix content used to live in a separate helix-design-system repo. The DSM edited markdown there, and it was hand-ported into the component library by an engineer — a slow, drift-prone two-repo dance. That's gone. The canonical content now lives here in content/, is edited in place, and the viewer renders it directly. No porting, no second repo.


The two-layer model (read this first)

Every fact about the design system has exactly one owner. Knowing which layer owns a fact tells you where to edit it.

LayerLives inOwned byHolds
Structured factspackages/helix-ui/src/metadata/*.metadata.tsEngineeringVariants, props, anti-patterns, a11y attributes, token bindings — anything a machine parses. Feeds the MCP server and the viewer's auto-rendered "Spec" tables.
Prose & guidancecontent/ (this directory)You (DSM)Rationale, when-to-use, gotchas, patterns, token narratives — the human story.

The viewer merges both at render time: it draws the structured "Spec" panel from metadata and renders your markdown around it.

[!IMPORTANT] Don't restate structured fields in prose. The variant list, props table, and a11y attributes are rendered automatically from metadata. If you also type them into the markdown, the two will drift. Write the why and the how, not the what — the table already shows the what.

A CI gate (verify:docs, below) fails the build if the layers fall out of sync, so neither side can ship a change that silently breaks the other.


Where content lives

content/ is flattened — no system/ nesting (an artifact of the old repo that's been removed). Each subdirectory maps to a section of the viewer:

DirectoryHolds
components/One page per component (58 of them) + component-template.md for new specs
tokens/Color, typography, spacing, radii, shadows, motion, breakpoints + machine-readable tokens.json
patterns/Cross-component patterns (component-match, layouts, filtering, table-behavior)
rules/Hard rules: accessibility, tech-stack, project-structure, governance
a11y/Accessibility requirements by component category
decisions/Recorded design decisions + rationale
icons/Icon inventory and usage
tasks/Task playbooks AI agents load (create-component, review-ui, etc.)

The loader is apps/docs/src/lib/system.ts: it reads content/<category>/<slug>.md directly, using each file's # H1 (or frontmatter) as the title. A README.md in a folder becomes that section's "Overview."


The authoring loop

edit content/*.md  →  npm run docs:dev  →  see it instantly  →  verify  →  commit
  1. Edit the relevant markdown file in content/.
  2. Preview — from the repo root, run npm run docs:dev and open the local viewer. It reads content/ live; your edits show on save. No build step, no porting.
  3. Verify — run npm run verify:docs (see below) before committing.
  4. Commit on a branch and open a PR. CI re-runs the gate.

[!TIP] Writing rationale, gotchas, and guidance is your highest-value work — it's the layer no metadata table can capture. Lean into the why.


The no-drift gate (verify:docs)

scripts/verify-docs.mjs enforces the two-layer contract. Run it with npm run verify:docs (it needs the package built first — npm run build).

It fails the build when:

  1. Coverage — a component exists in metadata but has no content/components/ page, or vice versa (minus a small list of declared exceptions, like the template).
  2. Variant drift — a variant declared in a component's metadata isn't mentioned anywhere in that component's markdown. (So when engineering adds a variant in code, the docs must be updated before it can ship.)

It warns (but won't block) when: a metadata token binding references a token missing from tokens.css, or a component slug is missing from figma-map.json / registry.json.

What this means for you: if you add a new component page, engineering must add its metadata in the same change (or the coverage check fails) — coordinate the two. If you rename or remove a component's variant in prose, make sure it still names every variant metadata declares.


Adding a new component spec

  1. Copy components/component-template.md to content/components/<slug>.md.
  2. Write the prose — rationale, when-to-use, composition guidance, gotchas. Leave the structured facts (variants/props/a11y) to metadata; the Spec panel renders them.
  3. Coordinate with engineering so the matching packages/helix-ui/src/metadata/<slug>.metadata.ts lands in the same PR — otherwise verify:docs fails on coverage.
  4. Mention every variant metadata declares (so the variant-drift check passes).
  5. npm run verify:docs, then PR.

For proposing a brand-new component or pattern (not just documenting one), use the governance process in rules/governance.md and the rules/proposal-template.md.


What you own vs. what engineering owns

You edit (here in content/)Engineering edits (in packages/helix-ui/)
Usage guidance, when-to-use, rationaleComponent code (src/components/)
Gotchas, anti-pattern narrativesStructured metadata (src/metadata/*.metadata.ts)
Token narratives & design intentToken values (src/tokens/, tokens.css)
Patterns, decisions, a11y proseVariants, props, a11y attributes (in metadata)

When in doubt: if a machine needs to parse it deterministically, it's metadata (engineering); if a human needs to understand the reasoning, it's prose (you).


Single brand, light + dark. Edit in place, preview live, let the gate keep the two layers honest.