ENFORCEMENT

The design system is enforced in CI — rules are not documentation, they are gates.

CI Gates (PR A)

Four new steps run in build-and-gate before the build:

| Gate | Script | What it checks | |---|---|---| | Token build | pnpm tokens:build | Style Dictionary builds without error | | Token drift | pnpm tokens:check | design-system/dist/ matches source JSON | | Boundary lint | pnpm lint:token-boundary | No primitive refs in component CSS | | No-magic-values | pnpm lint:no-magic-values | No raw hex/px/ms/z-index in module CSS | | Contrast | pnpm lint:contrast | All WCAG AA pairs pass |

Token Boundary Lint

File: scripts/lint-token-boundary.mjs

Rejects any .module.css file that uses a CSS primitive var when a semantic alias exists.

Forbidden (primitives with semantic aliases):

Allowed (no semantic layer exists):

No-Magic-Values Lint

File: scripts/lint-no-magic-values.mjs

Rejects raw values that should be token references:

Exceptions are documented in scripts/lint-no-magic-values.allowlist.json.

Naming Convention

Primitives MUST NOT use the --ds-color- prefix. That prefix is reserved for semantic tokens. The boundary lint allowlist var(--ds-color-*) works correctly only if this convention holds.

Adding a New Component

  1. Create design-system/components/NewComponent/ with 4 files (.tsx, .module.css, .test.tsx, index.ts)
  2. Export from design-system/index.ts
  3. Use only semantic tokens in .module.css — boundary lint will fail otherwise
  4. Add ## NewComponent heading in app/design-system/components/page.mdx — the check:component-docs gate will fail if missing
  5. Write Playwright visual baseline in tests/e2e/design-system-components.spec.ts