Skip to content

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, and Link, 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.mjs via pnpm run catalog.
  • The catalog script reads from *.stories.tsx files — stories are the source of truth.
  • Run pnpm run catalog after 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, or Internal.
  • 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 @deprecated JSDoc tag to the component export, then move the story to ZZ - 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.

  • 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).