Chapter 43: ARIA States & Properties ๐ŸŽ›๏ธ

Building an Accessible Live Search / Autocomplete

Capstone widget architecture: Synchronizing `role="combobox"`, `aria-autocomplete`, `aria-activedescendant`, `aria-controls`, and live result announcements.

LEARNING OBJECTIVES โŒต
  • Implement the complete W3C WAI-ARIA 1.2 Combobox Design Pattern.
  • Master the Virtual Focus Mechanism using aria-activedescendant without losing native DOM focus on the <input>.
  • Coordinate multiple ARIA states in real time: aria-expanded, aria-selected, aria-autocomplete, and aria-controls.
  • Connect a polite live region announcer (role="status") to inform screen readers of dynamic search result counts.
๐ŸŽฌ INTERACTIVE VISUAL PIPELINE Core Architecture Simulation
๐ŸŒ
1. Input
Directives & Tags
โš™๏ธ
2. Parse
Tokenizer & AST
๐ŸŒณ
3. Layout
Box Model & Flow
๐ŸŽจ
4. Render
GPU Paint & Composite
PHASE 1: INPUT & DIRECTIVES
Browser receives declarative markup stream, parsing tag tokens and initializing component state.

๐Ÿ“– The Mental Model & Story (Intuitive Foundation)

Imagine sitting in a library reading room with a research librarian assistant:

  1. The Question Sheet (The Combobox Input): You hold a pen and paper. You write: "Quantum". Native physical focus never leaves your hand or your pen.
  2. The Librarian's Quick Reference Tray (The Popup Listbox): The librarian instantly places a tray of 5 book cards on your desk.
  3. The Laser Pointer (aria-activedescendant): Instead of ripping the pen out of your hand and dragging your body to the shelf (which would break your typing flow), the librarian shines a red laser pointer onto Card #1: "Quantum Computing Fundamentals".
  4. The Whisper Assistant (The ARIA Live Announcer): The librarian gently whispers in your ear: "5 matching books found. Use up and down arrows to browse."

When building an autocomplete search widget, moving physical DOM focus (element.focus()) back and forth between the input box and dropdown <li> elements is a terrible anti-pattern: it destroys the user's cursor position, interferes with IME typing (Japanese, Chinese, Korean keyboards), and causes severe screen reader lag.

The aria-activedescendant Virtual Focus Pattern keeps native DOM focus locked onto the <input> at all times while projecting a virtual focus pointer onto the dropdown options.


Technical Deep Dive & Specifications

The ARIA 1.2 Combobox Anatomy

+-----------------------------------------------------------------------------------------------+
|                                  WAI-ARIA 1.2 COMBOBOX TOPOLOGY                               |
+-----------------------------------------------------------------------------------------------+
|                                                                                               |
|   <label id="search-label" for="search-input">Search Repositories</label>                     |
|                                                                                               |
|   <input                                                                                      |
|     id="search-input"                                                                         |
|     type="search"                                                                             |
|     role="combobox"                     <-- Declares widget type                              |
|     aria-labelledby="search-label"      <-- Accessible Name                                   |
|     aria-autocomplete="list"            <-- Autocomplete behavior                            |
|     aria-expanded="true"                <-- Dropdown visibility state                         |
|     aria-haspopup="listbox"             <-- Declares type of popup                            |
|     aria-controls="results-listbox"     <-- Target popup ID                                   |
|     aria-activedescendant="opt-2"       <-- Virtual focus pointer                             |
|   >                                                                                           |
|                                                                                               |
|   <ul id="results-listbox" role="listbox" aria-label="Suggestions">                           |
|     โ”œโ”€โ”€ <li id="opt-1" role="option" aria-selected="false">react</li>                         |
|     โ”œโ”€โ”€ <li id="opt-2" role="option" aria-selected="true">react-dom</li> <== [VIRTUAL FOCUS]  |
|     โ””โ”€โ”€ <li id="opt-3" role="option" aria-selected="false">react-router</li>                  |
|   </ul>                                                                                       |
|                                                                                               |
|   <div id="search-status" role="status" aria-atomic="true" class="sr-only">                   |
|     "3 repositories found. Use up and down arrows to navigate."                               |
|   </div>                                                                                      |
|                                                                                               |
+-----------------------------------------------------------------------------------------------+

The 4 Values of aria-autocomplete

Value Behavior Description
"list" Input text stays as typed; suggestions are presented in an external popup listbox. (Standard Google / GitHub search).
"inline" The system automatically appends predicted text inline into the input box past the cursor.
"both" Combines both: displays a popup list and autocompletes text inline into the input.
"none" Freeform input with no autocomplete prediction.

How aria-activedescendant Works Under the Hood

  1. Native hardware focus stays on the <input>. The user can continue typing or backspacing without interruption.
  2. When the user presses ArrowDown, JavaScript sets:
    input.setAttribute('aria-activedescendant', 'opt-2');
    
  3. The browser looks up #opt-2 in the Accessibility Tree, checks its role (role="option"), name, and aria-selected state, and fires a focus event to the screen reader.
  4. The screen reader announces: "react-dom, 2 of 3, selected".
  5. JavaScript toggles a visual active CSS class (.is-active) on #opt-2 and updates aria-selected="true".

๐Ÿ’ป Interactive Code Playground

Starter Code

Line-by-Line Code Breakdown

  • Line 66โ€“75 (<input role="combobox" ...>): Sets up the WAI-ARIA 1.2 Combobox pattern with aria-autocomplete="list", aria-expanded="false", aria-haspopup="listbox", and aria-controls="framework-listbox".
  • Line 77โ€“82 (<ul id="framework-listbox" role="listbox" ... hidden>): The controlled popup listbox containing individual options.
  • Line 85 (<div id="search-status" role="status" ...>): A pre-rendered polite live region that announces result counts dynamically upon typing.
  • Line 124 (status.textContent = '${items.length} suggestions available...'): Provides prompt auditory context without interrupting typing.
  • Line 169 (input.setAttribute('aria-activedescendant', opt.id)): Shifts virtual focus cleanly to the active option while physical keyboard focus remains seamlessly inside the <input>.

Expected Screen Reader Render Output


SYS: ACTIVE
HULL: 98%
CORE: STABLE
NET: ONLINE
HTML STARSHIP CODE TERMINAL playground.html
LIVE RENDER & DIAGNOSTICS CORE TEMP: 45ยฐC
INSPECTING DOM: VALID
TAGS: SCANNING...
[User types "re"]
Live Announcer speaks: "4 suggestions available. Use up and down arrows to navigate."

[User presses ArrowDown]
Screen Reader speaks: "React, 1 of 4, selected"

[User presses ArrowDown again]
Screen Reader speaks: "React Native, 2 of 4, selected"

[User presses Enter]
Input populated with "React Native", dropdown closes, live region announces "Selected React Native."

๐Ÿ‹๏ธ Hands-On Exercise

๐ŸŽฏ The Challenge: Add Keyboard Autopopulate & Escape Revert

Instructions:

  1. Extend the combobox playground above.
  2. Track the user's raw typed query in a variable userTypedText.
  3. When the user navigates options using ArrowDown / ArrowUp, temporarily reflect the active option's text inside input.value without submitting.
  4. When the user presses Escape:
    • Revert input.value back to userTypedText.
    • Close the listbox (aria-expanded="false").
    • Clear aria-activedescendant.
    • Announce: "Search restored to [userTypedText]" in the live region.

๐Ÿ Starter Code Sandbox

SYS: ACTIVE
HULL: 98%
CORE: STABLE
NET: ONLINE
STARTER CODE SANDBOX exercise.html
LIVE RENDER & DIAGNOSTICS CORE TEMP: 45ยฐC
INSPECTING DOM: VALID
TAGS: SCANNING...

โš ๏ธ Common Pitfalls

  1. Moving DOM Focus to the Dropdown Options: Never call optionElement.focus(). Focus must remain on the <input> so the user can continue typing without re-clicking.
  2. Missing aria-selected="true": Setting aria-activedescendant is not enough; you must also toggle aria-selected="true" on the targeted option.
  3. Spamming Live Regions on Fast Keystrokes: Typing rapidly can queue dozens of live region announcements. Always debounce search result announcements by 300ms.

๐Ÿ’ก Pro Tips

  1. ARIA 1.2 vs ARIA 1.0 Combobox Specs: In ARIA 1.0, role="combobox" was placed on a wrapper <div> with an <input> child. In ARIA 1.2, role="combobox" is placed directly on the <input> element itself. Always use the ARIA 1.2 pattern for modern web applications.
  2. Scroll Into View Alignment: When traversing long suggestion lists, use opt.scrollIntoView({ block: 'nearest' }) so that the focused option is always visible in the listbox without causing the whole webpage to scroll.

๐Ÿ“Œ Key Takeaways

  • The WAI-ARIA 1.2 Combobox places role="combobox" directly on the <input>.
  • aria-activedescendant creates a virtual focus pointer, allowing options to be selected without moving physical DOM focus away from the input.
  • Synchronize aria-expanded, aria-controls, and aria-selected on every keystroke.
  • Use a polite live region (role="status") to announce result count metrics to screen readers.
  • Support comprehensive keyboard commands: ArrowDown/ArrowUp (traverse), Enter (select), Escape (dismiss/revert).
  • --
โญ LEARN: HTML ๐ŸŒŸ โš”๏ธ QUIZ BATTLE ARENA // ACTIVE
3x
STREAK!
BONUS ACTIVE
COMBO
? Question 1 / 3

Why does the WAI-ARIA Combobox pattern use aria-activedescendant instead of shifting native DOM focus (element.focus()) to each option?

Question 1 / 3 Topic: HTML Fundamentals
00:45 REMAINING
XP REWARD
+250 XP
3x
STREAK!
BONUS ACTIVE
COMBO
? Question 2 / 3

Where should role="combobox" be placed according to the modern WAI-ARIA 1.2 specification?

Question 2 / 3 Topic: HTML Fundamentals
00:45 REMAINING
XP REWARD
+250 XP
3x
STREAK!
BONUS ACTIVE
COMBO
? Question 3 / 3

What value of aria-autocomplete indicates that suggestions are rendered in a separate popup list without automatic inline text completion?

Question 3 / 3 Topic: HTML Fundamentals
00:45 REMAINING
XP REWARD
+250 XP