CLAUDE.md did not name the overzicht context that RD-03 created, and it did not name the max-lines budget that RD-02 enforces. An agent that follows it writes a long page into the wrong context, and meets a red build with no warning. CLAUDE.md now names the context, the @overzicht/* alias, the overzicht -> registratie arrow, the 250-line budget and its glob, and the step-component contract. layers.mdx gains the same arrow. atomic-design.mdx credited eslint.config.mjs with the layer rules. dependency-cruiser enforces them. Each tool is now named for what it does. The page also gains the layer-folder table and the step contract. Four paths were pre-monorepo: three example paths in the ui-component skill, and two citations of libs/shared/src/ui/async, which RD-27 moved to libs/shared/src/ui/molecules/async. npm run ci --full passes: 67 and 45 storybook suites, 306 axe tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2.5 KiB
2.5 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 libs/shared/src/ui/ and libs/shared/src/layout/ — a new page should be
composition of existing blocks. Only add a block when nothing fits.
Rules
- Folder = atomic layer:
libs/shared/src/ui/atoms → molecules → organisms;libs/shared/src/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 typedinput()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. thealertatom). - Tokens only —
var(--rhc-*)/var(--app-*), never hardcoded colors (npm run check:tokensfails the build; escape hatch:token-okmarker + reason). - No hardcoded Dutch in shared components — expose copy as
input()s with$localizedefaults; the domain caller supplies the text (seelibs/shared/src/ui/molecules/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:
libs/shared/src/ui/atoms/button/— typed variant API overbtnclasses. - Molecule:
libs/shared/src/ui/molecules/async/— slot directives, localizable input defaults, spread constant. - Template:
libs/shared/src/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