How to: add a component
The full rules are CONVENTIONS.md §4; this is the short, task-oriented version.
Where it lives
A directory under grain/components/<layer>/<name>/ (design system) or tjakoen.github.io/view/components/<layer>/<name>/ (app-specific), layer ∈ atoms / molecules / organisms.
The checklist
- [ ]
<name>.html— the template, using the binding vocabulary (data-field,data-bind-<attr>,each, polymorphicslot-tag prop-as=). Header comment:<!-- <layer>/<name> — … -->. - [ ]
<name>.css— component-owned styles. One root class, variants as attributes (.btn[data-variant="soft"], never.btn.soft) — never a hardcoded color, onlyvar(--token)s. - [ ]
<name>.md— the catalog doc:# Name, prose,## Section+ fenced HTML examples. This is what makes the component self-documenting at/catalog— there's no separate registration step. - [ ]
<name>.ai.md— only if the component's AI-mode behavior is distinct enough to need its own panel (otherwise the catalog grain-flips the Human view automatically). - [ ] If it's an addressable surface that accepts actions, declare
data-kind+data-accepts="verb …"on the root — seeMAKE-A-SURFACE-OPERABLE.md.
CSS-only components
Some components have no .html (a class + docs only) — deliberate for layout shells/patterns (app-shell, tab-bar, chat-log) and data-driven atoms (b-badge, b-list). The .html file is only required for components batch/render expands as a tag. If a CSS-only component needs parent context to work (e.g. chat-message needs a chat-log's flex column), state that in its .md — an unstated layout dependency is a silent-failure trap.
Verify it landed
bun run dev
# then open /catalog — the new component appears automatically, no registration
bun run check && bun testIf the component wires a data-action, the boot-time drift guard in server.ts will warn on startup if that verb isn't in ai/contract.ts's ACTIONS registry.