ADR-010: Component Architecture & Catalog
Decision
Every design-system component lives in a three-layer model with downward-only dependencies, carries a standard interaction-pattern name, is discovered through a generated machine-readable catalog, and advances through a fixed maturity lifecycle.
Key rules
Folder structure and layers
- Three layers, dependencies flow downward only: Layer 1 Foundations (
src/foundations/) → Layer 2 UI components (src/components/ui/) → Layer 3 Application Patterns (src/components/patterns/). Each layer may depend only on the layers below it. - No Layer 3 pattern may be imported by a Layer 2 component.
- Foundations are tokens and global style rules, not React components.
- Cross-cutting families (charts, shared utilities) are exempt from layer ordering but must not create circular dependencies.
- Classify every new component into the correct layer before placing it in the file system.
- Public exports go through
src/index.ts.
Naming and composition
- Use standard interaction-pattern names, not domain-specific ones:
Chip(not Pill),SegmentedControl(not ToggleRow),RadioGroup(not RuleGroup),TextField(not InputField). - Pattern components may compose
Button,TextField,Select,RadioGroup,SegmentedControl, andLink, but must not fork their APIs. - All components use CVA for variants,
cn()for class merging, and Tailwind for styling. - Shared UI components stay dumb and prop-driven.
- WYSIWYG-specific UI may wrap primitives for authoring behavior but still inherits the token system.
Catalog
- The component catalog is a machine-readable JSON index of every design-system component, auto-generated by
scripts/generate-component-catalog.mjsviapnpm run catalog. - The catalog script reads from
*.stories.tsxfiles — stories are the source of truth. - Run
pnpm run catalogafter any story addition, removal, or rename. - Never hand-author or manually edit the generated catalog output.
- The catalog is what agents read to discover existing components before building anything new; it prevents duplication and enables the component-decision audit step.
Maturity and versioning
- Components carry exactly one maturity label, used verbatim in story tags:
Experimental,Beta,Stable,Deprecated, orInternal. - Internal components are not exported from
src/index.ts. - Experimental components may have breaking API changes without a major version bump.
- Never delete a component without first completing the deprecation phase.
- Deprecation checklist: audit usages, replace all usages, add an
@deprecatedJSDoc tag to the component export, then move the story toZZ - Deprecated/.
Why
A three-layer model with downward-only dependencies keeps the import graph acyclic and makes each component’s altitude explicit before it lands on disk. Standard interaction-pattern names and a generated catalog let both people and agents find an existing primitive instead of reinventing one, which is the main defense against duplication. A fixed maturity lifecycle makes deprecation a safe, auditable process rather than a sudden deletion. This record consolidates ADR-010 (layers), ADR-010 (catalog), and ADR-010 (maturity) into ADR-010; the naming guidance (Chip, SegmentedControl, RadioGroup, TextField) is retained as-is because it already matches current dmwd-io practice and the global hard rules.
Applies when
You are creating, renaming, restructuring, classifying, or deprecating any component in the design system, deciding whether a component is a Layer 2 primitive or a Layer 3 pattern, reviewing cross-layer imports, or regenerating the component catalog after a story change.
Related
- ADR-009 — icon role, placement & layout (component layout invariant).
- ADR-008 — border, surface & overlay discipline (component surface rules).
- ADR-003 — typography & density (component type and spacing classes).