Files
Edwin van den Houdt cf44bda0ce
Some checks failed
CI / frontend (push) Successful in 2m16s
CI / storybook-a11y (push) Successful in 4m6s
CI / backend (push) Failing after 48s
CI / api-client-drift (push) Successful in 1m32s
docs(skills): extract house recipes as Claude Code skills for SSP templating
8 template-generic skills in .claude/skills/ (new-feature, new-context,
value-object, form-machine, bff-endpoint, mutation-command, ui-component,
new-ssp), condensed from CLAUDE.md/ARCHITECTURE/fp-tea/ADRs and pointing at
this repo's worked examples. CLAUDE.md gains a pointer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 10:10:38 +02:00

2.4 KiB

name, description
name description
ui-component 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 onlyvar(--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 <name>.stories.ts, title prefixed with the layer:

const meta: Meta<ButtonComponent> = { title: 'Atoms/Button', component: ButtonComponent };
export const Primary: StoryObj<ButtonComponent> = { 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

npm run lint && npm run check:tokens && npm run build
npm run storybook   # eyeball the story; CI will run axe