← Back to the catalog

Skill · Version 1.0.0

Keyboard and focus behavior

Designs predictable keyboard navigation and visible focus across widgets, overlays, deletion and route changes.

keyboard-focus-engineering

See how it helps

When is it useful?

Use when mouse-free navigation gets trapped or loses its current position.

What does it help you do?

  • Keeps a logical tab order and avoids positive tabindex workarounds.
  • Chooses the appropriate focus model for composite widgets.
  • Restores meaningful focus after closing, deleting or navigating.

Try asking this

Fix a table-row action dialog so closing returns focus correctly, including when its row was deleted. Delete the originating row while its dialog is open. On closing, place focus on a meaningful remaining table control instead of the removed button.

Detailed documentation

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

Details…Show less

Keyboard & Focus Engineering

Every interactive user journey must work end-to-end with a keyboard alone, with a clearly visible focus indicator, predictable tab order and deliberate focus restoration when overlays open, close or remove elements (WCAG 2.1.1, 2.1.2, 2.4.1, 2.4.3, 2.4.7, 2.4.11).

1. Tab Order & Tabindex Discipline

  • DOM order drives focus order: arrange elements in the DOM so Tab and Shift+Tab follow the visual reading and task sequence (WCAG 2.4.3). Avoid CSS reordering (flex-direction: row-reverse, arbitrary order or absolute positioning) that disconnects visual order from DOM tab order.
  • Tabindex rules:
    • Prefer native focusable elements (<button>, <a href>, <input>, <select>, <textarea>, <summary>).
    • Use tabindex="0" only to add a custom widget's active entry point to the tab sequence or make a scrollable region keyboard-scrollable.
    • Use tabindex="-1" for elements targeted by programmatic .focus() (headings, dialog containers or inactive items inside a roving-tabindex composite widget).
    • Never use positive tabindex (tabindex="1", 2, etc.) — it hijacks page-wide tab order.
  • Bypass blocks (WCAG 2.4.1): provide a working "Skip to main content" link as the first focusable item on shells with repeated navigation.

2. Composite Widgets: Roving Tabindex vs. aria-activedescendant

Complex widgets (tablists, menus, toolbars, radio groups, tree views, grids) must be a single tab stop:

  • Tab / Shift+Tab moves into and out of the composite widget as a whole; Arrow keys, Home and End move between items inside it.
  • Roving tabindex pattern: give the active item tabindex="0" and all sibling items tabindex="-1". On arrow key press, move tabindex="0" to the target item, set the previous item to -1, and call .focus().
  • aria-activedescendant pattern (for comboboxes where focus stays on the text <input>): keep DOM focus on the <input>, update aria-activedescendant="option-id" on ArrowDown/ArrowUp, and scroll the active option into view (scrollIntoView({ block: 'nearest' })).

3. Focus Lifecycle in Overlays, Deletions & Route Changes

Plan where focus goes before, during and after every structural UI transition:

  • Opening a modal dialog or drawer:
    1. Save document.activeElement before opening.
    2. Move initial focus inside the dialog (first primary field, primary action, or dialog heading/container with tabindex="-1").
    3. Contain Tab/Shift+Tab inside the modal (<dialog>.showModal(), inert on background siblings, or focus trap) and close on Escape.
  • Closing a dialog or popover: restore focus to the opener, or to the nearest logical parent/trigger if the opener was unmounted.
  • Deleting an item from a list or table: never let focus drop to <body>. Move focus to the next sibling item, the previous item if the last one was deleted, or the list heading / "Add" button when empty.
  • Hidden and inert content: ensure elements inside closed drawers, inactive tabs or collapsed panels are unfocusable (hidden, inert or display: none). Never place a focusable element inside an aria-hidden="true" container.

4. Visible Focus Indicators (WCAG 2.4.7 & 2.4.11)

  • Use :focus-visible to render a high-contrast focus ring (at least 2px solid outline with outline-offset and >= 3:1 contrast).
  • Never use outline: none without a visible :focus-visible replacement that survives forced-colors: active mode.
  • Prevent sticky headers or footers from obscuring focused elements (scroll-margin-top / scroll-margin-bottom).

Included files 1

  • SKILL.md

About this skill package

keyboard-focus-engineering

keyboard-focus-engineering: Remove accessibility barriers through semantic markup, keyboard and screen-reader support, usable forms and regression checks.

Details…Show less

keyboard-focus-engineering

keyboard-focus-engineering: Remove accessibility barriers through semantic markup, keyboard and screen-reader support, usable forms and regression checks.

Includes the complete skill and its supporting files (1 files). The exact authored scope is preserved below.

Original skill description (source language)

Use when engineering keyboard operability, tab order, roving tabindex, visible focus indicators and predictable focus lifecycle across dialogs, menus and dynamic views.

Install the skill, review its source and trust settings, then enable it for the relevant project or agent.

Requirements

  • Orqestria installed on your computer.