Skip to content

Design System — @dmwd-io/design-system

FieldValue
TypeAgent Reference
Source~/.copilot/agents/_refs/expert-react-frontend-engineer/design-system.md
DescriptionNot specified

Source Content

Design System — @dmwd-io/design-system

This file is the field guide for working with the dmwd-io design system. All component work must pass this knowledge before touching code.

Three-Layer Architecture (ADR-010)

Dependencies flow downward only. Never import from a higher layer.

LayerLocationStyleStrictness
1. Foundationssrc/foundations/, src/styles/, tailwind.config.tsToken-firstStrict — ADR amendment required to change
2. UI Componentssrc/components/ui/ (incl. cards/, selection/, dropdowns/)Atomic + composed, prop-drivenReusable
3. Application Patternssrc/components/patterns/ (incl. cards/)Composed workflowsOpinionated for a specific UX workflow
Cross-cuttingsrc/components/charts/, src/components/shared/Chart family + shared typesFamily-scoped

Layer 1 — Foundations

The single source of truth for color, typography, spacing, motion, icons, accessibility, and shape.

  • No hardcoded hex, px, or font-family in component files — use CSS custom properties or Tailwind token classes.
  • Every foundation has a Storybook gallery story.
  • Motion tokens include prefers-reduced-motion fallbacks.
  • Changes require an ADR amendment because they cascade everywhere.

Layer 2 — UI Components

Dumb and prop-driven. No fetching, no domain state, no business rules.

FolderFamilyKey examples
ui/ rootGeneral primitivesButton, Badge, Banner, Callout, Chip, Dialog, Drawer, Popover, Tooltip, Accordion, Tabs, Pagination, Skeleton, Spinner, Toast, Avatar, Heading, RichText, Divider, DataGrid, Table, CommandPalette, SlideOutPanel, SlideOutWizard, PanelWizard, SideNav, TopBar
ui/cards/Card primitivesContentCard, IconCard, ListCard, MediaCard, NotificationCard, PricingCard, ProfileCard, StatCard, TestimonialCard
ui/selection/Selection familyToggle, RadioGroup, RadioBoolean, SegmentedControl, Select, ChoiceCard
ui/dropdowns/Dropdown familyActionDropdown, ComboboxDropdown, MultiSelect, SearchableSelect, TreeSelect

Rules:

  • Variant API via CVA. Class merging via cn().
  • Every prop has a Storybook control with realistic dummy data.
  • Keyboard support, visible focus ring, ARIA attributes, no color-only meaning.
  • A component family in a subfolder shares hooks, styles, and variants through index.ts.

Layer 3 — Application Patterns

Composed workflows that encode a specific UX workflow. Compose Layer 2 — never re-implement primitives.

  • May hold local UI state (open/closed, sort direction, current step) but never server or domain state.
  • May accept slot-based or render-prop children, but defaults must work out of the box.
  • Storybook sidebar: only use “Application Patterns” as the top-level label, not “patterns” as a subfolder label anywhere else.

Where new components go

What you are buildingLocation
New token, semantic class, governance ruleLayer 1 — requires ADR
Reusable, prop-driven control or composed UI blockLayer 2 src/components/ui/
New card primitivesrc/components/ui/cards/
New selection controlsrc/components/ui/selection/
New dropdown variantsrc/components/ui/dropdowns/
Workflow that composes existing UI for a specific use caseLayer 3 src/components/patterns/
Chart variant or utilitysrc/components/charts/
Cross-component shared typesrc/components/shared/types.ts

Component Naming Conventions (ADR-010)

Use the standard interaction pattern name, not domain-specific names:

WrongRightReason
PillChipConflicts with the squared-off shape language
ToggleRowSegmentedControlPrecise interaction pattern name
RuleGroupRadioGroupStandard pattern name
FooterNavStepNavigationDescribes the wizard nav pattern
QuestionHeaderSectionHeaderReusable beyond question contexts
InputFieldTextFieldAligns with ARIA and Material conventions

Interaction Patterns Reference (ADR-010)

PatternComponentWhen to use
Single selection (compact)RadioGroupSimple text options, ≤6 items
Single selection (rich)ChoiceCardOptions with title + description + tags
Multi-toggle (≤5 options)SegmentedControlBinary or small enum choices
Multi-toggle (>5 options)SelectToo many options for segments
Status indicatorBadgeInline status: done, active, pending, warning
Selectable tagsChipFilterable/removable tags, categories
Text navigationLinkInline references and secondary navigation
Primary actionButtonSave, continue, export, add, confirm

Design Token System (ADR-001)

Color palette

TokenLightDarkUsage
--primaryTeal 168° 79% 25%Teal 168° 65% 50%Primary actions, success states
--brand-navyNavy 216° 44% 20%Navy 214° 46% 78%Display headings, dark card backgrounds
--brand-amberAmber 38° 92% 58%Amber 41° 92% 64%Warnings, attention states
--brand-mistOff-white 44° 35% 96%Dark 217° 22% 18%Page backgrounds, subtle surfaces
--destructiveRed 5° 74% 48%Red 8° 80% 63%Errors, danger callouts
--infoBlue 209° 66% 37%Blue 209° 60% 60%Informational callouts
--surface-accentSoft accent-tintedSoft accent-tintedAccent-tinted surfaces, chips
--surface-strongTheme-specific darkTheme-specific darkTop bars, strong cards, high-emphasis panels
--surface-strong-foregroundWhiteWhiteText on --surface-strong

Semantic tones for component APIs (ADR-006)

Public component variant props use semantic tones. Never palette names in shared component APIs:

// GOOD
<Badge tone="success" />
<Callout tone="warning" />
<Banner tone="info" />
// BAD — palette name leaks into API
<Badge tone="teal" />
<Callout tone="amber" />

Allowed tones: info, success, warning, danger, accent

Typography (ADR-001, ADR-003)

  • Headings: Fraunces Variable (serif), weight 500–700
  • Body: Source Sans 3 Variable (sans-serif), weight 400–600

Governed semantic type classes (use these, not raw text-*):

ClassUse
ui-type-labelSection labels, row labels, compact metadata
ui-type-body-xsMeta copy, timestamps, compact helper text
ui-type-body-smStandard product body inside cards, rows
ui-type-bodyLonger product prose
ui-type-title-smCard titles, picker titles
ui-type-title-mdSection headers, panel headers, dialog titles
ui-type-title-lgHigher-emphasis product headings
ui-type-title-xlRare large headings
ui-type-displayHero-level, explicit display only
ui-type-value-mdNumeric emphasis in product cards
ui-type-value-lgPricing or KPI emphasis

Density / spacing (ADR-003)

Governed density classes (use these, not raw p-5/6):

ClassUse
ui-pad-rowChoice rows, list items, interactive rows
ui-pad-cardDefault product cards and compact panels
ui-pad-card-roomyLarger cards that stay within product density
ui-pad-panelDrawers, dialogs, grouped content panels
ui-pad-shellScreen shells and larger page-local wrappers

Shape language (ADR-002)

Token classValueUsage
rounded-xl12pxCards, panels, modals, floating surfaces
rounded-lg10pxInputs, buttons, menu items, embedded controls
rounded-md8pxBadges, chips, segmented options, compact tags
rounded-sm6pxTiny inline tokens, preview swatches, helper labels
rounded-full50%Circles ONLY: step markers, radio dots, icon discs, handles

No pill shapes. No arbitrary radius values.

Theme Governance (ADR-006)

Components reference semantic surface tokens, not hardcoded brand colors:

  • --surface-accent for accent-tinted surfaces
  • --surface-strong for high-emphasis themed surfaces
  • --surface-strong-foreground for text on strong surfaces

Adding a new theme: write an ADR → add [data-accent="<name>"] + night variant in src/index.css → populate all palette and semantic surface tokens → validate in Storybook across day/night.

Storybook Authoring Rules

Setup

import type { Meta, StoryObj } from '@storybook/react-vite'
import { createComponentDocs } from '@/lib/storybook-docs'
import { MyComponent } from './my-component'
const meta = {
component: MyComponent,
tags: ['autodocs'],
parameters: {
docs: {
description: {
component: createComponentDocs({
summary: 'One-line summary.',
when: 'When to use this component.',
whenNot: 'When NOT to use this component.',
}),
},
},
},
} satisfies Meta<typeof MyComponent>
export default meta
type Story = StoryObj<typeof meta>
export const Default: Story = {
args: {
// realistic data
},
}

Border truthfulness (ADR-008)

  • Do NOT wrap stories in rounded-xl border border-border bg-card chrome unless the border belongs to the component itself.
  • If a story shows a component inside a bordered host, name it explicitly: Inside Card, In Table Cell.
  • Use spacing, max-width, and tonal contrast instead of fake borders for preview separation.

Compound components in Storybook (ADR-011)

Register all sub-components in meta.subcomponents. Use the compound API directly in render functions — no function Demo() wrapper:

const meta = {
component: FilterPanel,
subcomponents: {
'FilterPanel.Search': FilterPanel.Search,
'FilterPanel.DateRange': FilterPanel.DateRange,
'FilterPanel.Status': FilterPanel.Status,
},
tags: ['autodocs'],
} satisfies Meta<typeof FilterPanel>
export const Default: Story = {
render: (args) => (
<FilterPanel {...args}>
<FilterPanel.Search placeholder="Search..." />
<FilterPanel.DateRange label="Date filed" />
<FilterPanel.Status options={mockOptions} />
</FilterPanel>
),
}

After changing stories

Run pnpm run catalog after any story title change, addition, or removal. Update .storybook/preview.tsx ordering if the sidebar order changes.

Validation pipeline (run for any component change)

Terminal window
pnpm lint
pnpm typecheck
pnpm test
pnpm run readiness:check
pnpm build
# For Storybook changes:
pnpm run catalog
pnpm build-storybook
pnpm test:visual

Never claim a check passed unless it was run in the current session.

Don’t Reinvent the Wheel (ADR-014)

For behavior-heavy components, wrap a mature library before building custom. The repo already relies on:

DomainLibrary
Tabular stateTanStack Table
ChartsRecharts
Code editingMonaco
Low-level overlay behaviorRadix Popover

When adding a new behavior-heavy component (dropdown, calendar, command palette, drag-and-drop, upload zone, terminal, virtualized list), check the repo-level don't-reinvent-wheel.md shortlist first.

Wrapper rules: keep tokens/spacing/motion/copy in the design-system layer, constrain the public API to cases this system supports, do not expose every upstream option as a prop.

Compound Components (ADR-011)

Pattern selection

QuestionYES →NO →
Do all items have the same semantic role?Array args (Pattern A)Compound components (Pattern B)
Are all item fields required or uniformly optional?Pattern APattern B
Would adding a new item type require an optional field or discriminator?Pattern BEither
Is the count of items fixed by structure?Pattern BEither

Pattern A — array args (homogeneous)

// Every item has the same shape and same role
<FactsBody facts={[{ text: '...' }, { text: '...' }]} />

No discriminator fields (type, kind, variant). No optional fields only relevant to some items.

Pattern B — compound components (heterogeneous)

<ParentingPlanBody guidingStatement="...">
<ParentingPlanBody.Schedule period="School Year">
<ParentingPlanBody.WeekdayCustody>
Mother has primary care Mon–Thu.
</ParentingPlanBody.WeekdayCustody>
<ParentingPlanBody.WeekendCustody>
Father has alternating weekends.
</ParentingPlanBody.WeekendCustody>
</ParentingPlanBody.Schedule>
</ParentingPlanBody>

Implementation: Object.assign, each sub-component one file, index.tsx is the only public export. Sub-components are never exported bare from the package.

ADR Quick Reference

ADRTopicKey rule
ADR-001Design tokensSemantic tokens, Fraunces/Source Sans, radius scale
ADR-010Component architectureCVA+cn(), naming conventions, interaction patterns
ADR-002Shape languageNo pills, restrained rectangles, rounded-full circles only
ADR-010Layered architectureThree layers, downward imports only
ADR-010Component catalogpnpm run catalog after story changes
ADR-006Theme governanceSemantic surface tokens, ADR-gate for new themes
ADR-014Don’t reinvent wheelWrap mature libraries for behavior-heavy components
ADR-010Component maturityStability labels in Storybook
ADR-011Variant governanceMultiple variants expected; each documents when to use
ADR-004Premium polish & motionCSS tokens first, prefers-reduced-motion, one border per stack
ADR-005Testing & a11y gatesWCAG 2.2 AA, axe CI, keyboard tests, visual regression
ADR-003Typography & densityui-type-* and ui-pad-* semantic classes
ADR-008Storybook border truthfulnessNo fake border chrome in stories
ADR-009Icon conventionsleadingIcon, trailingIcon, triggerIcon, endAdornment
ADR-006Semantic color governanceSemantic tones in public APIs, not palette names
ADR-011Compound componentsPattern A vs B selection; Object.assign; no bare sub-exports