Add a card or a skin
Three generators do the registering, so a new card or skin passes npm run check and npm run test-storybook before
you change a line of it. Add --dry-run to any of them to see what it would write.
A new domain, in Orb
Section titled “A new domain, in Orb”npm run new-card -- fan --code fn --domain fan # a control: on and off, over fan.togglenpm run new-card -- co2 --code co --domain sensor --kind reading --unit ppm # a reading: a read-only numberA control is an orb and a pill that switch the entity; a reading is a bare icon and a figure, with no services. The generator writes the whole slice, the Home Assistant side included, and registers it everywhere:
packages/core/src/entities/fan/for the model,packages/core/src/ha/for the mapper, the actions, theuseHaFanhook and theHaFanheadless component, andpackages/core/src/seeds/fan.tsfor the demo seeds every skin’s stories draw on;src/skins/orb/cards/fan/for the card, its CSS and the standard stories, andsrc/skins/orb/ha/HaFanTile.tsx;- Orb’s Lovelace card (
src/skins/orb/lovelace/), its exports, and the spec’s card and deploy pages.
Then make it the real thing: the entity’s fields, the card’s drawing, the mapper’s attributes and the actions’ services,
its section in spec/cards.md, and its page on this site, docs/src/content/docs/entities/fan.mdx. The page’s kind
frontmatter (its sidebar group and order, a summary for the home page, the companions it reads with) is all
the site needs: the home page, the Entities overview, the Hooks reference and the YAML guide list it from there and from
the code, and the site’s build fails until the page exists. verify/orb-conformance.test.ts says which other step is
missing.
An existing domain, in Orb
Section titled “An existing domain, in Orb”npm run new-card -- <name> --code <xx>Without --domain, for a domain whose model and hook already exist, it writes only Orb’s side: the card drawn in the
card frame, its tile, stories on the domain’s seeds in core, src/skins/orb/ha/Ha<Name>Tile.tsx, and its place in
Orb’s Lovelace card. Both generators read the domain from one description, the same one the standalone scaffolder’s
add command reads.
An existing domain, in Field
Section titled “An existing domain, in Field”npm run new-card -- <name> --skin fieldFor a domain Orb draws and Field doesn’t yet. Its model and hook already exist, so this writes only Field’s drawing and adapters: the module, its CSS, icons,
tests and stories on the domain’s seeds in core, src/skins/field/ha/Ha<Name>Module.tsx, and the module’s place in
Field’s Lovelace card. A domain with an action a press can give starts as a control, one key per action, where an
action that asks first opens the panel on its question instead; one without starts as a reading, a glyph and today.
What a domain declares it sets, a level, a choice or an on-off switch, is drawn too: a level as a fader, a choice or a
switch as keys in the panel; in Orb, a level as the card’s groove and the rest as chips in its sheet.
The generator refuses, saying why, a domain whose hook needs an option it can’t know.
verify/field-conformance.test.ts holds the result to the same steps.
A new skin
Section titled “A new skin”npm run new-skin -- canvasIt creates src/skins/canvas/, a workspace package named @dashhaus/canvas, with neutral tokens, its parts, fixed
footprints, dashboard furniture, its Storybook groups, a custom:dashhaus-canvas Lovelace card, tests and
spec/canvas.md, and registers it with Storybook, the bundle and the deploy config. The empty dashboard is a valid
start. Add domains with the card generator:
npm run new-card -- light --skin canvasA generated skin’s cards start read-only, or with the hook’s zero-argument actions, rather than inventing behaviour. Give the skin its look by replacing its tokens and parts.
The page around the cards comes with it. Its tokens key dark mode on data-canvas-theme, the skin’s own attribute,
which the Lovelace card sets from Home Assistant and the Theme toolbar sets in Storybook, so another skin’s
theme never leaks into it. shared/surface.ts names its web font once: the Lovelace card links it on Home Assistant’s
page and Storybook links it for the skin’s stories. And CanvasActionStatus shows a command Home Assistant refused,
with Retry and Dismiss, so a failed tap never goes unnoticed.
A study
Section titled “A study”A study is not made with new-skin: a study run has a fresh agent build a skin the way someone outside the repository
would, from the release tarballs, a picture or a description, and a simulated Home Assistant.
npm run study -- canvas --image ./reference.png # or --describe "warm paper, ink and brass"npm run study:keep -- canvas # once the agent is doneThe first packs the release, sets up a sandbox and prints the task to hand a fresh agent; the second brings the skin it
built into src/skins/ as a study, with the same Storybook groups as every skin, its design rules, the agent’s report
of every guess and dead end, and screenshots.
Studies lists every one.
For a skin outside the repository, with no Storybook or workspace, use the published scaffolder instead: Build a skin.
Storybook’s shape
Section titled “Storybook’s shape”Every skin’s section has the same groups in the same order: Cards, Bundles (Orb only), Dashboards,
Foundations, Connected. Every card has the same stories under the same names: its own states, then
Unavailable, Worst case, All states, Sizes, Footprints, Compact, Details, Details, unavailable and Details, phone.
verify/storybook.test.ts checks both.