Design Systems & Markup Contracts
design systems & markup contracts design system markup contracts specify the html each component emits — enabling design system
Introduction
Design system markup contracts define the allowed HTML patterns for buttons, forms, dialogs, and cards — not just Figma pixels. Components are APIs; their DOM structure is a semver surface. Breaking heading order or button types in a patch release causes a11y regressions across 40 products.
Business problem
Business pressure: Rebrand launches with new React components but ad hoc DOM — screen reader users lose form labels, SEO loses semantic headings. A markup contract lets design, eng, and QA share one source of truth validated in CI.
- Consistency: Same button semantics in marketing HTML, app React, and email templates where possible.
- Compliance: WCAG audits reference component HTML specs — not one-off pages.
- Velocity: Authors compose from documented patterns instead of inventing div-buttons.
Why this feature exists
Platform motivation: Design systems matured from CSS libraries to component libraries. HTML semantics are the interoperability layer between CSS, JS, assistive tech, and crawlers — contracts make that explicit.
- History: Bootstrap markup → React component libs → web components + Storybook a11y addon.
- Alternative rejected: "Developers will figure out semantics" — yields 200 variants of broken dialogs.
- Modern role: Contract tests snapshot DOM; linters forbid anti-patterns in PRs.
Browser internals
Inside the engine: Design tokens affect CSS; markup contracts affect accessibility tree and default behaviors. A <button type="button"> vs <div role="button"> differs in activation, form submission, and focus handling without any JavaScript.
- Implicit roles: Native elements carry roles — prefer them over ARIA replication.
- Form association:
<label for>+idmust be stable across renders.
Rendering workflow
Rendering path: Server or client renders component → outputs contract HTML → CSS attaches → enhancement adds behavior. Visual regression tests pixels; contract tests assert DOM shape.
- SSR/SSG: Contract must hold in static HTML output, not only after hydrate.
- Slots: Web components use light DOM slots — document slot markup in contract.
Feature deep dive
Contract contents: Required elements, attribute rules, heading levels, focus trap requirements for modals, and forbidden patterns (nested interactive elements).
- Button: Always
<button>or<a>with href — document loading states witharia-busy. - Dialog:
<dialog>or role=dialog with labelled-by, focus trap spec. - Card: Article vs div — when to use
<article>for SEO.
<!-- Design System: PrimaryButton v2 markup contract --><button type="button" class="ds-btn ds-btn--primary" data-ds="PrimaryButton"><span class="ds-btn__label"><!-- slot --></span></button><!-- FORBIDDEN: div.ds-btn, nested button, missing type -->
Accessibility analysis
A11y architecture: Contracts encode WCAG success criteria — colour contrast is token job; names/roles/keyboard are markup jobs. Storybook a11y and axe run on every component story against contract HTML.
- Keyboard: Document tab order for composite widgets (tabs, combobox).
- Announcements: Live region markup baked into Toast contract.
SEO impact
SEO architecture: Marketing uses design system HTML in CMS — heading components enforce single h1. Link components enforce descriptive text, not "click here".
- Semantic tags: Card vs article decision affects rich snippet eligibility.
- Image component: Requires alt prop in contract — empty alt only for decorative.
Security considerations
Security boundary: Rich text components define sanitization allowlists in contract — which tags CMS authors may use.
- Link: Contract validates rel=noopener on external targets.
- Embed: iframe component requires sandbox attribute set.
Performance impact
Performance: Markup depth affects layout cost — contracts can limit nested wrappers. Image component enforces width/height against CLS.
- CLS: Media components require dimensions in HTML attributes.
- DOM size: Alert component — single node vs nested divs — document max depth.
Real production example
Production pattern: GOV.UK Design System and Material publish HTML examples. Internal DS ships Storybook + jest-axe + custom ESLint rule no-div-button on all repos consuming package.
- Versioning: Markup breaking change = major semver; codemod provided.
- CMS: WYSIWYG restricted to contract tag allowlist.
Enterprise usage
Enterprise: Design platform team gates releases on contract test suite. Legal reviews modal consent markup once — reused everywhere.
- Docs: Each component page shows "DOM output" tab alongside React/Vue API.
- Audit: Quarterly crawl compares prod DOM samples to contract snapshots.
Common production failures
What breaks in prod: Minor DS release changed modal from dialog to div — focus trap broke; WCAG audit failed. Or Image component dropped width attribute — CLS regression site-wide.
- Incident: Accordion used h3 for styling only — heading outline nonsense; fixed in contract v3.
Architecture review questions
- Is DOM output documented and snapshot-tested for each component?
- What markup changes require major semver?
- Does CMS output match component contracts?
- Are forbidden patterns linted in consumer repos?
Hands-on project
Project: Write markup contract for Modal + TextField; implement HTML/CSS; add jest snapshot + axe test.
- Deliverable: Contract doc, Storybook story, failing test for div-button anti-pattern.
Interview questions
What belongs in a design system markup contract vs CSS tokens?(Advanced)
Tokens: colour, spacing, typography. Contract: element choice, ARIA when necessary, heading levels, focus behavior, sanitization allowlists, and semver policy for DOM changes. Both are public APIs.
Follow-up: How do you migrate breaking markup across 30 apps?
Try it yourself
Edit the HTML, CSS, or JS panels — the preview updates as you type.
Try it yourself
Summary
Design system markup contracts specify the HTML each component emits — enabling a11y, SEO, security, and perf guarantees at scale. Platform teams enforce contracts with snapshots, linters, and major version discipline.