Accessibility as a Contract: Angular Aria Instead of Guesswork In short: Angular 22 ships — headless, unstyled directives that implement the WAI-ARIA patterns properly. Roll-your-own roles and key handlers are now the slow, buggy option. When a developer finishes styling a component, it usually looks done. Then QA files a ticket saying a screen reader announces nothing, or that you cannot reach the control with , and somebody sprinkles and on until the ticket closes. That approach fails for a reason worth naming: accessibility is behaviour, not attributes. A listbox is not a . It is arrow keys moving a focus ring, and jumping to the ends, typeahead matching, tracking, and focus returning correctly when the list closes. Nobody remembers all of that, which is why hand-written versions are almost always subtly wrong. Angular 22 made this a solved problem. Start with native HTML Before reaching for anything, use the element that already does the job. The version cannot be reached with , does not respond to or , is not announced as a button, and does not respect the user's "reduce motion" or high-contrast settings the way a native control does. Every one of those is a separate thing you would have to build. Rule 1: use , , , , whenever they fit. Style them however you like — you can remove every default style without removing the behaviour. When native HTML runs out Native HTML has no listbox with typeahead, no combobox, no tab set, no tree, no menubar. This is where teams historically wrote several hundred lines of key handling per component and got it 80% right. , stable since Angular 22, provides these as headless directives — behaviour and ARIA wiring with no styling at all. The patterns available: Category Patterns --- --- Search and selection Autocomplete, Listbox, Select, Multiselect, Combobox Navigation and actions Menu, Menubar, Toolbar Content organisation Accordion, Tabs, Tree, Grid "Headless" is the important word. These directives give you roles, keyboard handling, focus management, and the ARIA state attributes. They give you zero appearance. Your design system stays entirely yours. This is also what distinguishes it from Angular Material: Material is a styled component library built on the CDK; is the unstyled behaviour layer for teams building their own design system. For a component platform like ours, that is exactly the right level. Rebuilding the grade selector properly The from the first lesson used a row of buttons. That works, but semantically it is three unrelated buttons — a screen reader user has no idea they are a related set with one active choice, and there is no arrow-key movement between them. It is a listbox. Here it is with : The and classes are yours — style them as pills, cards, or anything else. What you no longer write: and on the right elements kept in step with the visual state roving so the group is one tab stop, not three Arrow key handling, with wrapping, in the right axis for / Typeahead — typing "rej" jumps to Reject pointing at the focused option versus actually removing from the tab order is worth knowing: selects whatever the user arrows onto, requires or . For a destructive-ish choice like rejecting a milk batch, is correct — you do not want arrowing past "Reject" to reject it. Where a native element is still right Not everything needs a pattern. The valve toggle from our dispatch screen is a genuine two-state switch, and a native with is the whole implementation: Because the state is bound, the visual class and cannot drift apart — they read the same signal. And because the host is a real , and work with no key handler. The judgement call: Need Use --- --- Click target, on/off, link, text entry Native element Roving focus among a set of related options Anything with a popup, tree, or grid of cells A styled, batteries-included component set Angular Material "But I thought adding ARIA attributes made a component accessible" This belief is everywhere, and it is encouraged by tooling: automated checkers flag missing text and missing labels, you add them, the report goes green, and it feels finished. The trap is that ARIA attributes describe behaviour; they do not create it. is a promise to the assistive technology: this element behaves like a listbox. The screen reader then tells the user "listbox, three items, Grade A selected" and — crucially — the user now expects arrow keys to move through the options. If you did not implement arrow keys, you have made the experience worse than a plain . Before, the user got no promise and could explore. Now they were told it is a listbox, they press Down, nothing happens, and there is no way to reach the second option at all. Here is what that looks like concretely: An automated accessibility scanner passes this. It has a role, valid children, a label, and . And a keyboard user cannot select Grade B at all — the options are not focusable, so there is no way in. That is the part that catches teams out: automated checks verify the…