mgifford/accessibility-skills/keyboard
Claude Code and agent skill by mgifford/accessibility-skills
- community
Checking that the SKILL.md file is still in the repository.
Load this skill for every project containing interactive UI elements — buttons, links, modals, dropdowns, sliders, tabs, carousels, or any custom widget. Under no circumstances create an interactive component that cannot be fully operated by keyboard alone. Absolutely always ensure visible focus indicators, logical tab order, and no keyboard traps. Apply these rules to every interactive element without exception.
Install
npx vskill@latest install mgifford/accessibility-skills/keyboardSKILL.md
Keyboard Accessibility Skill
Canonical source:examples/KEYBOARD_ACCESSIBILITY_BEST_PRACTICES.mdinmgifford/ACCESSIBILITY.mdThis skill is derived from that file. When in doubt, the example is authoritative.
Apply these rules to every interactive UI element and feature.
Core Mandate
All interactive functionality must be fully usable with a keyboard alone — no mouse or touch required, except where the underlying function genuinely depends on path-based movement. Keyboard access is not equivalent to pressing Tab through every control — native and composite widgets use different key conventions, and users must understand how to operate custom widgets.
Severity Scale (this skill)
| Level | Meaning | |
|---|---|---|
| Critical | Blocks task completion entirely for keyboard and AT users | |
| Serious | Significantly impairs keyboard access; workaround unreasonable | |
| Moderate | Creates friction for keyboard users; workaround exists | |
| Minor | Best-practice gap; marginal keyboard impact |
Do not assign severity from the rule alone — determine the actual effect on the task. Consider whether the defect prevents reaching, operating, or leaving a component; whether it blocks an essential task or only an optional feature; whether focus is lost or placed misleadingly; and whether a reasonable keyboard method remains available.
Critical: No Keyboard Trap
If focus can enter a component, users must be able to move focus away using Tab, Shift+Tab, arrow keys, or Escape (depending on the component). If leaving requires an unusual command, provide instructions before or within the component.
A keyboard trap with no exit is Critical. Restricting focus temporarily is NOT a trap when it is a modal dialog with a standard/documented exit: focus moves in on open, Tab/Shift+Tab stay within it, a keyboard-operable control closes/completes it, and closing returns focus to the invoking control or another logical location.
Test embedded editors, third-party widgets, iframes, media players, remote desktops, and canvas applications carefully — they commonly consume Tab or other keys and need an available, documented exit.
Critical: All Interactive Elements Must Be Keyboard Reachable
Every element that can be activated by mouse must be reachable and activatable by keyboard. Use native elements — they provide keyboard behaviour, focusability, semantics, states, and browser integration built in at zero extra cost:
Adding role="button" and tabindex="0" does not add button behaviour by itself — a custom button must also implement Enter and Space activation, disabled state, focus styling, and the expected accessible name/state. Test the actual result, not merely whether a keydown handler exists. Mouse and touch handlers must not be the only way to operate a control.
Critical: Expected Key Behaviours
Deviating from expected widget key behaviour is Critical — it breaks the mental model AT users depend on. Do not override standard browser behaviour on native controls without a documented need. Follow the current WAI-ARIA APG for custom widgets (versioned independently of WCAG — APG patterns are design guidance, not WCAG requirements by themselves; always confirm the current version).
| Control | Required keys | |
|---|---|---|
| Button | Enter, Space | |
| Link | Enter | |
| Checkbox | Space to toggle | |
| Radio group | Arrow keys to move between options; Space to select | |
<select> | Browser/platform conventions for navigation and selection | |
| Menu / menubar | Arrow keys; Enter to activate item; Escape to close | |
| Tab widget | Arrow keys between tabs; Tab moves into the active panel | |
| Dialog | Escape to close; focus trapped inside while open | |
| Combobox | Depends on editable/select-only behaviour; Escape commonly closes | |
| Tree view | Up/Down navigate visible nodes; Right expands/enters; Left collapses/parent | |
| Slider | Arrow keys to change value; Home/End for min/max; Page Up/Down for larger steps | |
| Grid | Arrow keys move among cells; Tab usually enters/leaves the composite | |
<details> summary | Enter/Space toggles disclosure |
Do not implement a widget from a summary table alone — use the complete current APG pattern, document any variation, and test with representative browsers and assistive technologies.
Critical: Dialog Focus Management
Incorrect dialog focus management is Critical — keyboard and screen reader users lose their place, or cannot reach dialog controls at all.
Preferred approach: native <dialog> with showModal()
A modal dialog opened with showModal() places the rest of the document in an inert state automatically — this is now the preferred pattern over manually applying inert to every sibling.
Initial focus depends on the dialog's content — a short confirmation might focus Cancel; a long informational dialog might focus a heading with tabindex="-1" so users can read from the beginning. Do not always focus the first interactive element without considering the content. If the trigger is removed while the dialog is open, return focus to the nearest logical workflow location rather than the document body.
Do not add aria-modal="true" to a container and assume it creates modality — ARIA does not make content inert, contain focus, add keyboard handling, or restore focus.
Native modal behaviour does not remove the need to test: initial focus location, Tab/Shift+Tab containment, Escape and visible close controls, form submission/cancellation, focus restoration, nested/stacked overlays, screen reader announcement, and browser support in the project's support matrix.
Manual approach (when native <dialog> is unavailable)
The inert attribute prevents all interaction (focus, click, AT) with elements outside the open dialog. It has good browser support (baseline 2023) and is simpler and more reliable than manual focusable-element cycling:
For environments without inert, use the `focus-trap` library (production-tested) rather than hand-rolling focus-cycling logic, or use the `wicg-inert` polyfill.
Critical: Non-Modal Disclosures and Popovers Must Not Trap Focus
Do not trap focus in ordinary disclosures, tooltips, menus, or non-modal popovers unless the interaction pattern explicitly requires restricted focus. For simple show/hide content, a button with aria-expanded is sufficient:
When open content overlays the page: ensure keyboard users can enter and leave it; close non-persistent content when interaction moves away; support Escape where users reasonably expect it; return focus when closing would otherwise cause focus loss; prevent the overlay from obscuring the new focus location; do not open complex content merely because its trigger receives focus.
The HTML Popover API can be used where it meets the requirement — still test focus behaviour and dismissal for the selected popover type.
Serious: Focus Visibility
Every focusable element must have a clear, persistent visible focus indicator. Removing focus outlines without an equally visible replacement is Serious.
Distinguish the applicable requirements:
- 2.4.7 Focus Visible (AA): a visible mode of operation in which focus is visible
- 1.4.11 Non-text Contrast (AA): component states may need 3:1 contrast against adjacent colours
- 2.4.13 Focus Appearance (AAA): an enhanced minimum indicator area and 3:1 change between focused/unfocused pixels — do not describe this AAA formula as an AA requirement
A 3px solid outline with adequate contrast is a useful project default, not the complete Focus Appearance formula. Test focus in light, dark, increased-contrast, and forced-colours modes, and on every background a control appears against.
Serious: Focus Not Obscured (WCAG 2.4.11/2.4.12)
Sticky headers, sticky footers, cookie notices, chat panels, toolbars, virtual keyboards, and persistent disclosures can obscure the focused element even though focus is technically visible. A focused element fully hidden behind a sticky overlay is Serious.
CSS scroll spacing helps but does not prove conformance — test every breakpoint, zoom level, text-spacing configuration, and persistent overlay. WCAG 2.4.11 (AA) requires the focused element is not entirely hidden; partial obscuring is Moderate, complete hiding is Serious. Prefer designs where the complete focused component and its indicator remain visible — that exceeds the AA minimum and aligns with 2.4.12 Focus Not Obscured Enhanced (AAA).
Serious: Focus Order
Tab order must follow logical reading and interaction sequence. Illogical focus order is Serious — screen reader users build a spatial model of the page from the focus sequence, and out-of-order focus breaks that model.
- Use semantic DOM order as the primary mechanism; align visual order with DOM
order — do not use CSS order, grid placement, or absolute positioning to create a misleading visual sequence
- Never use positive
tabindexvalues — they create a separate, hard-to-maintain
focus sequence
tabindex="0"— only when an element legitimately belongs in sequential
focus order and no native element provides the semantics
tabindex="-1"— only for programmatic focus targets and inactive items in
roving-tabindex patterns
- Do not add non-interactive headings/paragraphs/containers to the Tab order
without a specific interaction requirement
- Do not use
autofocusunless placing initial focus there is expected, tested,
and won't surprise users
- When content is inserted, removed, reordered, or filtered, preserve focus or
move it to a logical location — never leave focus on a deleted element
Serious: Composite Widgets (Roving Tabindex)
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 100 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingskills/keyboard/SKILL.md
Popularity
- 7-day trend score
- 0
Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/mgifford/accessibility-skills/keyboard)