Custom interactive components that lack accessible names, roles, or state expose AT users to components they cannot identify or operate. A screen reader encountering a nameless div with an onClick handler reads nothing useful; a toggle without aria-checked gives no feedback on state. WCAG 2.2 SC 4.1.2 (Name, Role, Value) is Level A — failures break keyboard and AT navigation entirely, affecting users of VoiceOver, NVDA, and JAWS, plus voice control users who activate elements by speaking their visible label.
Critical because components with no accessible name or role are invisible or unpredictable to screen reader users, breaking core functionality and violating WCAG 2.2 SC 4.1.2 at the most foundational Level A.
Prefer native HTML elements that carry implicit semantics. When building custom components, always supply all three ARIA pillars — name, role, and state.
// Custom toggle — explicit role + name + live state
<div
role="switch"
aria-checked={isEnabled}
aria-label="Enable email notifications"
tabIndex={0}
onClick={() => setIsEnabled(v => !v)}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') setIsEnabled(v => !v)
}}
/>
// Dropdown trigger — name from visible text + state
<button aria-haspopup="listbox" aria-expanded={isOpen}>
Sort by
</button>
For every custom component in your codebase, verify it exposes name via aria-label or visible text, role via a semantic element or explicit role=, and relevant state via the matching aria-* attribute.
ID: accessibility-wcag.perceivable.aria-attributes
Severity: critical
What to look for: Enumerate every relevant item. Check custom components (buttons, toggles, dropdowns, tabs) for ARIA attributes. Verify that every interactive component has: 1) an accessible name (from aria-label, aria-labelledby, visible text, or implicit semantic role), 2) a correct role (e.g., role="button" if not a native button), and 3) a state value (e.g., aria-expanded, aria-checked, aria-pressed).
Pass criteria: At least 1 of the following conditions is met. All interactive components expose accessible name, role, and state. Native semantic HTML (button, input, select) requires no additional ARIA if label is visible. Custom components have explicit role and aria-* attributes.
Fail criteria: Custom components lack role attribute, or interactive elements lack accessible names, or state is not exposed via ARIA.
Do NOT pass when: The item exists only as a placeholder, stub, or TODO comment — partial implementation does not count as passing.
Skip (N/A) when: Never — interactive components require accessible names and roles.
Cross-reference: For broader data handling practices, the Data Protection audit covers data lifecycle management.
Detail on fail: Example: "Custom MenuButton component has no role='button', no aria-label, and no aria-expanded. Filter dropdown lacks aria-expanded when state changes"
Remediation: For native HTML, ensure labels are present and associated with inputs:
<label htmlFor="email">Email</label>
<input id="email" type="email" />
For custom components, add ARIA:
<button
aria-label="Toggle navigation menu"
aria-expanded={isOpen}
onClick={() => setIsOpen(!isOpen)}
>
Menu
</button>