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.
The root HTML element on the code side. The canvas is unaffected (frames carry no element semantics).
ARIA role, when it differs from the element’s native role.
See role by prop.
See element by prop.
Role by propGeneratedCurated#
An ARIA role driven by an enum prop. Banner’s canonical case: status: error → role="alert", status: info → role="status". Code emits a lookup; roleByProp overrides role.
{
"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
{
"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.
{
"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}"
}
}
}
}