← Back to A11y
AgentA11y

Skill · Version 1.0.0

Accessible interactive components

Builds dialogs, menus and other interactive controls with keyboard behavior, meaningful labels and predictable focus management.

accessible-component-development

See how it helps

When is it useful?

Use when a custom menu or dialog works with a mouse but blocks keyboard or screen reader users.

What does it help you do?

  • Matches each widget's ARIA role to its required keyboard interactions.
  • Restores focus after dialogs close and prevents interaction with their background.
  • Makes tables, autocomplete options and tooltips understandable beyond mouse hover.

Try asking this

Fix our account menu and confirmation dialog. Check arrow keys, Escape, focus restoration and screen reader names. After cancellation, focus should return to the account button. Opening the dialog must prevent keyboard navigation into the background page.

Detailed documentation

Read the skill’s original instructions, examples and checks in their authored language.

Details…Show less

Accessible Component Development

Custom interactive components fail for keyboard or screen reader users when ARIA attributes are added without implementing their required keyboard and focus state machine. Every ARIA role is a behavioral contract.

1. Modal & Non-Modal Dialogs (dialog / alertdialog)

  • Prefer native <dialog> opened with .showModal(), or role="dialog" with aria-modal="true".
  • Name the dialog via aria-labelledby pointing to its visible title heading; use aria-describedby only for short confirmation text (as in role="alertdialog"), not entire complex forms.
  • Required behavior:
    • Background content is inert (inert on app root siblings) so neither Tab nor screen reader virtual cursor escapes behind the backdrop.
    • Escape closes the dialog (unless an explicit unsaved-changes guard intercepts it).
    • Closing via Escape, Close button or backdrop restores focus to the trigger element.

2. Disclosure, Accordions, Menus & Popovers

Distinguish between a disclosure (shows/hides arbitrary content) and a menu (an application action list):

  • Disclosure / Accordion:
    • Trigger is <button type="button" aria-expanded="true|false" aria-controls="panel-id"> (inside <h3><button>...</button></h3> for accordions).
    • Panel uses normal Tab navigation; never put role="menu" or menuitem on navigation link groups or filter panels.
  • Action Menu (role="menu" / menuitem / menuitemcheckbox / menuitemradio):
    • Trigger button has aria-haspopup="menu" and aria-expanded="true|false".
    • Keyboard open (Enter, Space, ArrowDown) focuses the first enabled menuitem.
    • ArrowDown/ArrowUp cycles items, Home/End jumps to first/last, character keys jump by first letter, Escape closes and restores trigger focus, and Tab closes the menu.

3. Tabs (tablist, tab, tabpanel)

  • Container has role="tablist" (aria-label/aria-labelledby, plus aria-orientation="vertical" if vertical).
  • Each tab is <button type="button" role="tab"> with aria-selected="true|false", aria-controls="panel-id", and roving tabindex (0 on selected, -1 on inactive).
  • Each panel has role="tabpanel" and aria-labelledby="tab-id", with hidden on inactive panels.
  • Arrow keys move between tabs (activating automatically on focus for lightweight panels, or via Enter/Space when switching triggers heavy network work).

4. Combobox / Autocomplete (role="combobox")

  • Text <input> has role="combobox", aria-expanded="true|false", aria-controls="listbox-id", aria-autocomplete="list|both" and aria-activedescendant tracking the highlighted <li role="option" id="...">.
  • Options reflect selection via aria-selected="true|false".
  • ArrowDown/ArrowUp navigates options without losing input cursor position, Enter selects the active option, Escape closes the popup, and empty states ("No results found") are announced cleanly.

5. Data Tables, Grids & Tooltips

  • Tables: use <table>, <caption> (or aria-labelledby), <th scope="col"> and <th scope="row">. For sortable columns, place a <button> inside the <th> and set aria-sort="ascending|descending|none" on the <th>.
  • Tooltips: a role="tooltip" linked via aria-describedby is for supplementary hints only. It must appear on both :hover and :focus-visible, stay hoverable, and dismiss on Escape without stealing focus (WCAG 1.4.13). Never place interactive controls inside a tooltip — use a popover/disclosure instead.

Included files 1

  • SKILL.md