Design System Contracts

Spec reference

Semantics & accessibility

What the component is to the platform: element, ARIA role, prop-driven variants of both, and the named exceptions the lint demands.

Element & roleGeneratedCurated#

The code renderer’s HTML element, and the ARIA role when it differs from the element’s native one. The element vocabulary is a closed enum — a contract cannot ask for an element the generator has no accessibility knowledge of.

elementrequired"button" | "span" | "div" | "a" | "input" | "article" | "section" | "header" | "footer" | "label" | "nav" | "hr" | "ul" | "li" | "p" | "textarea" | "select" | "fieldset" | "blockquote" | "code" | "kbd" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6"

The root HTML element on the code side. The canvas is unaffected (frames carry no element semantics).

rolestring

ARIA role, when it differs from the element’s native role.

roleExceptionstring

See declared role exceptions.

roleByProp{ prop: string; map: Record<string, string> }

See role by prop.

elementByProp{ prop: string; map: Record<string, string> }

See element by prop.

Role by propGeneratedCurated#

An ARIA role driven by an enum prop. Banner’s canonical case: status: errorrole="alert", status: inforole="status". Code emits a lookup; roleByProp overrides role.

contracts/banner.contract.json (excerpt · v1.0.0) — shipping contract, loaded at build time
{
  "semantics": {
    "element": "div",
    "roleByProp": {
      "prop": "status",
      "map": {
        "info": "status",
        "success": "status",
        "warning": "alert",
        "error": "alert"
      }
    }
  }
}

Element by propGeneratedCurated#

The rendered HTML element follows an enum prop — Heading’s level maps "2" → h2. Code emits an ELEMENT_MAP lookup and renders a dynamic tag; semantics.element is the fallback. The canvas is unaffected — a declared fidelity boundary.

Build-time guardrails: Each of these fails the build by name — the generator refuses, it never papers over (source: core/emit-react.ts validateContract, exercised by the C2 eval family).

  • the driving prop must be a declared enum
  • the map must cover every enum value
  • every mapped element must be in the code generator’s element vocabulary
contracts/heading.contract.json (excerpt · v1.0.0) — shipping contract, loaded at build time
{
  "semantics": {
    "element": "p",
    "elementByProp": {
      "prop": "level",
      "map": {
        "1": "h1",
        "2": "h2",
        "3": "h3",
        "4": "h4",
        "5": "h5",
        "6": "h6"
      }
    }
  },
  "props": [
    {
      "name": "level",
      "description": "Document outline level — drives the rendered element (h1–h6) and the size ramp.",
      "type": {
        "enum": [
          "1",
          "2",
          "3",
          "4",
          "5",
          "6"
        ]
      },
      "default": "2",
      "bindings": {
        "figma": {
          "kind": "VARIANT",
          "property": "Level",
          "values": {
            "1": "H1",
            "2": "H2",
            "3": "H3",
            "4": "H4",
            "5": "H5",
            "6": "H6"
          }
        },
        "code": {
          "prop": "level"
        }
      }
    }
  ]
}

Declared role exceptionsGeneratedCurated#

The native-semantics lint refuses a role that has a native HTML equivalent claimed on a non-native element — the imported-button principle: native elements over ARIA re-creation, always. Legitimate APG composites declare an exception: a one-sentence reason, carried in the contract (semantics.roleException for root-level claims, part.roleException per part), rendered on the spec sheet. Reviewable, never silent.

contracts/progress-bar.contract.json (excerpt · v1.0.1) — shipping contract, loaded at build time · the role rides anatomy.root.attrs; the exception names why
{
  "semantics": {
    "roleException": "Native <progress> cannot host the contract's styled track/fill anatomy (its rendering is only reachable through vendor pseudo-elements, outside the token vocabulary), so the progressbar role rides a div per WAI-APG."
  },
  "anatomy": {
    "root": {
      "attrs": {
        "role": "progressbar",
        "aria-label": "{label}"
      }
    }
  }
}