--- name: ui-component description: Add a shared UI building block (atom, molecule, organism) with its Storybook story. Use only after confirming no existing block in shared/ui fits — new blocks are the exception, composition is the default. --- # UI component (atom / molecule / organism) First: check `shared/ui/` and `shared/layout/` — a new page should be composition of existing blocks. Only add a block when nothing fits. ## Rules - **Folder = atomic layer**: `shared/ui/` atoms → molecules → organisms; `shared/layout/` templates. Each level only uses levels below. - Standalone component, **English name** (shared = language-agnostic), signal `input()`s only, `inject()` over constructor DI. - **Atoms are thin wrappers over CIBG Huisstijl (Bootstrap 5.2) classes** (`btn`, `form-control`, `card`, …) — you own a small typed `input()` API, the design system owns the visuals. No CIBG class for it? Hand-roll a small surface from the token bridge (ADR-0003, e.g. the `alert` atom). - **Tokens only** — `var(--rhc-*)` / `var(--app-*)`, never hardcoded colors (`npm run check:tokens` fails the build; escape hatch: `token-ok` marker + reason). - **No hardcoded Dutch** in shared components — expose copy as `input()`s with `$localize` defaults; the domain caller supplies the text (see `shared/ui/async`). - Components with content-projected slots export a spread constant so callers import one thing: `export const ASYNC = [AsyncComponent, AsyncLoadedDirective, …] as const;` ## Story (required — this is the component's test) Co-located `.stories.ts`, title prefixed with the layer: ```ts const meta: Meta = { title: 'Atoms/Button', component: ButtonComponent }; export const Primary: StoryObj = { args: { variant: 'primary' } }; ``` One named export per meaningful state. CI runs axe on every story (`test-storybook:ci`) — a11y failures break the build. Machines/wizards mount specific states via the `Seed` message. No heavy component specs; Storybook is the UI test surface. ## Worked examples - Atom: `src/app/shared/ui/button/` — typed variant API over `btn` classes. - Molecule: `src/app/shared/ui/async/` — slot directives, localizable input defaults, spread constant. - Template: `src/app/shared/layout/wizard-shell/` — the canonical wizard outline. ## Verify ```bash npm run lint && npm run check:tokens && npm run build npm run storybook # eyeball the story; CI will run axe ```