Skip to content
Back to skills

Wcag Keyboard Navigation

ASecurity

Reference catalog for WCAG 2.2 keyboard-navigation conformance - covers SC 2.1.1 (Keyboard), 2.1.2 (No Keyboard Trap), 2.1.4 (Character Key Shortcuts), 2.4.3 (Focus Order), 2.4.7 (Focus Visible), 2.4.11/2.4.12 (Focus Not Obscured) - with conformance levels (A/AA), test scripts, and per-criterion failure patterns, plus the modal focus-trap / focus-management pattern (focus-on-open, Tab-cycle, inert, Escape-closes, restore-to-trigger, native `<dialog>`) in references/. Use when authoring or rev...

  • 10 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 12, 2026
testingtypescript

Works with

  • cli

Security analysis

A100/100

Pro scans all 13 files and shows the line behind each finding

Scanned October 12, 2026

npx -y skills add testland/qa --skill wcag-keyboard-navigation --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Wcag Keyboard Navigation?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Wcag Keyboard Navigation
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/testland-wcag-keyboard-navigation/badge)](https://www.skillsdirectory.com/skills/testland-wcag-keyboard-navigation)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: wcag-keyboard-navigation
description: "Reference catalog for WCAG 2.2 keyboard-navigation conformance - covers SC 2.1.1 (Keyboard), 2.1.2 (No Keyboard Trap), 2.1.4 (Character Key Shortcuts), 2.4.3 (Focus Order), 2.4.7 (Focus Visible), 2.4.11/2.4.12 (Focus Not Obscured) - with conformance levels (A/AA), test scripts, and per-criterion failure patterns, plus the modal focus-trap / focus-management pattern (focus-on-open, Tab-cycle, inert, Escape-closes, restore-to-trigger, native `<dialog>`) in references/. Use when authoring or reviewing keyboard-only interaction support, or a modal / drawer / popover's focus management."
---

# wcag-keyboard-navigation

> Reference catalog for **how** to verify keyboard-navigation
> conformance. Pairs with the scanner umbrella
> `axe-a11y` (axe-core, pa11y, Lighthouse a11y).

## Overview

WCAG 2.2 organizes accessibility into four principles - 
**Perceivable / Operable / Understandable / Robust** - at three
conformance levels - **A / AA / AAA** ([wcag22][wcag22]).

[wcag22]: https://www.w3.org/TR/WCAG22/

Keyboard navigation lives under **Operable** (Principle 2) and
spans six key Success Criteria covering keyboard operability,
focus management, and focus visibility.

## When to use

- Reviewing a component's keyboard interaction surface during PR
  review.
- Authoring a keyboard-navigation test plan for a new component.
- Triaging an accessibility audit finding tagged with one of the
  SCs below.
- Configuring per-rule severities for
  `axe-a11y`.

## Success Criteria

### SC 2.1.1 - Keyboard (Level A)

Per [wcag22][wcag22]: "All functionality of the content is operable
through a keyboard interface" without requiring specific timings.
Path-dependent input (handwriting) is exempt.

| Pattern                                               | What to test                                          |
|-------------------------------------------------------|-------------------------------------------------------|
| `<button>`, `<a href>`, `<input>`                     | Native - already keyboard-accessible.                 |
| `<div onclick="...">`                                 | **Anti-pattern.** Non-focusable; not keyboard-operable. Convert to `<button>`. |
| Custom widget (`role="button"`, `tabindex="0"`)        | Verify Enter / Space activates the same handler.       |
| Drag-and-drop                                         | Provide a keyboard alternative (arrow keys + Enter, or button-based reorder). |

**Test script (Playwright):**

```typescript
test('SC 2.1.1 - interactive elements are keyboard-operable', async ({ page }) => {
  // Tab to the button, activate with Enter
  await page.keyboard.press('Tab');
  await page.keyboard.press('Enter');
  await expect(page.locator('[data-testid="action-result"]')).toBeVisible();

  // Repeat with Space for elements that should accept Space
  await page.keyboard.press('Tab');
  await page.keyboard.press('Space');
  await expect(...).toBeVisible();
});
```

### SC 2.1.2 - No Keyboard Trap (Level A)

Per [wcag22][wcag22]: "Focus must be movable away using standard
methods; users should be informed of exit procedures if non-
standard keys are required."

| Common failure                                          | Fix                                                   |
|---------------------------------------------------------|-------------------------------------------------------|
| Modal traps Tab without an explicit Escape handler       | Add `Escape` handler that closes the modal and restores focus. |
| Embedded `<iframe>` (e.g. third-party widget) traps Tab   | Set `tabindex="-1"` on the iframe OR document the exit (Esc + Tab) in surrounding label. |
| Custom date picker locks focus inside on first focus      | Provide Tab to exit the calendar and continue to the next field. |

See [references/focus-trap.md](references/focus-trap.md) for the
**intentional**-trap convention (modal focus management) which is
distinct from a violation.

### SC 2.1.4 - Character Key Shortcuts (Level A, added in 2.1)

Single-character shortcuts (`/` to focus search, `j`/`k` to
navigate items) must offer **at least one** of:

- **Off mechanism** - user can disable the shortcut globally.
- **Remap mechanism** - user can rebind to non-character or
  modifier-prefixed combinations.
- **Active only on focus** - shortcut active only when the
  associated component has focus.

This SC exists because users with speech-input software produce
spurious key presses; unguarded single-character shortcuts trigger
unintended actions.

### SC 2.4.3 - Focus Order (Level A)

Per [wcag22][wcag22]: components must "receive focus in an order
that preserves meaning and operability."

| Pattern                                        | What to test                                           |
|------------------------------------------------|--------------------------------------------------------|
| Reading-order vs DOM-order vs visual-order     | All three should agree. CSS `order` / `flex-direction: row-reverse` can desync visual from DOM. |
| `tabindex="3"` (positive)                      | **Anti-pattern.** Positive tabindex creates explicit focus order separate from DOM; rarely intended. Use `0` or `-1`. |
| Modal that doesn't trap focus                  | Tab moves focus to the page behind; user loses context. |
| Skip links                                     | Always at the start of the document; `tabindex="0"` not needed (anchor links are focusable). |

### SC 2.4.7 - Focus Visible (Level AA)

Per [wcag22][wcag22]: "Any keyboard operable user interface has a
mode of operation where the keyboard focus indicator is visible."

| Failure                                              | Fix                                                  |
|------------------------------------------------------|------------------------------------------------------|
| `*:focus { outline: none; }` without alternative      | Replace with `:focus-visible` style: `:focus-visible { outline: 2px solid var(--focus-ring); }`. |
| Subtle 1px gray ring on a gray background            | 3:1 contrast ratio against background per WCAG SC 1.4.11; high-contrast theme should bump to 4.5:1. |
| Disabled focus indicator on hover                    | Hover and focus styles are independent; never tie them. |

### SC 2.4.11 / 2.4.12 - Focus Not Obscured (Level AA / AAA, NEW in 2.2)

Per [wcag22][wcag22]: components receiving focus cannot be
"entirely hidden due to author-created content."

| Failure pattern                                              | Fix                                                  |
|--------------------------------------------------------------|------------------------------------------------------|
| Sticky header covers the focused input on long-form pages     | Adjust `scroll-margin-top` so focused element scrolls into view below the sticky header. |
| Persistent cookie banner covers focused element              | Modal / overlay should NOT obscure focused content; use compact banner or temporarily hide. |
| Auto-popup chat widget overlays focused fields                | Render below other content OR move to a corner that doesn't intersect with form fields. |

**2.4.11 (Minimum, Level AA):** focus must NOT be **entirely**
obscured.

**2.4.12 (Enhanced, Level AAA):** focus must NOT be **partially**
obscured. Stricter; relevant for high-stakes forms.

## Per-component test patterns

### Form

1. Tab through every field; verify focus order matches reading
   order.
2. Verify each interactive element has a visible focus ring.
3. Verify label-input association via `<label for="...">` or
   `aria-labelledby`.
4. Activate submit via Enter from any field.
5. After submit error: verify focus moves to the first invalid
   field (per SC 3.3.1 - Error Identification).

### Modal / dialog

1. On open: focus moves to the first interactive element
   (or to the dialog's close button if no other focusable element).
2. Tab cycles through dialog content only - does NOT escape to
   page behind.
3. Shift+Tab from first → last interactive element (cycle).
4. Escape closes the dialog and returns focus to the trigger.

(See [references/focus-trap.md](references/focus-trap.md) for the full
modal pattern.)

### Menu / dropdown

1. Tab focuses the trigger.
2. Enter / Space / Down-arrow opens the menu; first item focused.
3. Down/Up arrows navigate items; Home/End jump to first/last.
4. Enter / Space activates the focused item.
5. Escape closes the menu and returns focus to the trigger.

This matches the [ARIA Menu pattern][aria-menu] (referenced from
`aria-authoring-patterns`).

[aria-menu]: https://www.w3.org/WAI/ARIA/apg/patterns/menubar/

### Tabs

1. Tab focuses the active tab (one tabstop per tab group).
2. Left/Right arrows navigate between tabs (no Tab key).
3. Activation: automatic on focus OR explicit on Enter/Space
   (manual activation; ARIA Authoring Practices defaults).
4. After tab change: Tab moves to the panel content.

## CI integration

The patterns above translate to per-test assertions in
`axe-a11y` configurations. The
`a11y-violation-gate` skill
gates the build on new violations of these SCs.

## Anti-patterns

| Anti-pattern                                                  | Why it fails                                                       | Fix |
|---------------------------------------------------------------|---------------------------------------------------------------------|-----|
| `<div onclick>` for buttons                                    | Not focusable, not Enter-activatable.                              | Always `<button type="button">`. |
| `outline: none` for clean design                                | Removes focus indicator entirely (SC 2.4.7).                       | `:focus-visible { outline: 2px solid <color>; }`. |
| `tabindex="3"` to control focus order                           | Creates a non-DOM order; breaks on dynamic content.                | DOM-order tabbing; `tabindex="0"` for custom focusables, `-1` to skip. |
| Single-character shortcuts always-active                        | Triggered by speech-input users (SC 2.1.4).                         | Off / remap / focus-only. |
| Sticky header obscures focused input                            | Violates SC 2.4.11.                                                 | `scroll-margin-top` per element. |

## References

- [wcag22][wcag22] - WCAG 2.2 specification.
- [references/focus-trap.md](references/focus-trap.md) - focus
  management for modals (the intentional-trap pattern, 6 steps, native
  `<dialog>`, test scripts).
- `wcag-color-contrast` - 
  for SC 1.4.11 focus-indicator contrast.
- `aria-authoring-patterns` - for canonical interactive-widget patterns.
- `axe-a11y` - scanners that
  detect SC violations programmatically.

Files in this skill

  • .tessl-plugin/plugin.json728 B
  • SKILL.md10.6 KB
  • evals/aa-signoff-on-a-checkout-with-two-sticky-bars/criteria.json5.6 KB
  • evals/aa-signoff-on-a-checkout-with-two-sticky-bars/task.md8.8 KB
  • evals/kanban-cards-that-only-move-by-mouse/criteria.json5 KB
  • evals/kanban-cards-that-only-move-by-mouse/task.md8.7 KB
  • evals/power-user-shortcuts-archiving-mail-nobody-touched/criteria.json5.3 KB
  • evals/power-user-shortcuts-archiving-mail-nobody-touched/task.md7.2 KB
  • evals/procurement-audit-finding-on-our-hand-rolled-dialog/criteria.json5.8 KB
  • evals/procurement-audit-finding-on-our-hand-rolled-dialog/task.md8.7 KB
  • evals/tab-strip-where-arrows-move-the-panel-but-not-the-reader/criteria.json5 KB
  • evals/tab-strip-where-arrows-move-the-panel-but-not-the-reader/task.md8 KB
  • references/focus-trap.md9.8 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.