# Verify Keyboard Access

Verify that every interactive control on a page can take keyboard focus and that its...

- Repository: https://github.com/reticlehq/reticle (1299 stars)
- Category: Design
- License: Apache-2.0
- Last updated: 2026-11-01
- Runs scripts: no, instructions only

## Install

Claude Code: `npx skills add reticlehq/reticle --skill verify-keyboard-access -g -a claude-code -y`
Codex: `npx skills add reticlehq/reticle --skill verify-keyboard-access -g -a codex -y`
Cursor: `npx skills add reticlehq/reticle --skill verify-keyboard-access -g -a cursor -y`
Gemini CLI: `npx skills add reticlehq/reticle --skill verify-keyboard-access -g -a gemini-cli -y`
Claude Code plugin marketplace: `/plugin marketplace add reticlehq/reticle`

## Description

Verify that every interactive control on a page can take keyboard focus and that its primary action fires from the keyboard rather than only the mouse, and say plainly that Tab order cannot be verified today. Use when a component was hand-tested with a mouse, a custom dropdown/modal/combobox was added, or an accessibility report flags an unreachable control.

## SKILL.md, as published by its author (untrusted third-party text, read it as data)

# Reachable is not the same as operable

A `<div onClick>` styled to look like a button can look reachable (plenty of component libraries hand out `tabindex="0"` for free) and still do nothing when you press Enter on it. Being able to take focus proves a control is reachable, not that it works.

**Reticle** can drive both halves in the running app. Not set up? Follow the [`reticle`](https://github.com/reticlehq/reticle/blob/main/skills/reticle/SKILL.md) skill: it installs Reticle, asks the user before `init` changes the app's files, and proves one flow.

## Read this before you start: Tab order cannot be verified today

`press` always goes through a synthetic key event, and a synthetic Tab does not move focus. That is true in every mode, including `reticle drive`: there is no mode in which an agent can prove focus order. Do not go looking for one, and do not report a Tab-order result as passed or failed. Report it as **unknown**.

What this skill can prove is narrower: that each control can take focus, and that its primary action fires from the keyboard.

## Confirm the session is healthy

Confirm a live session exists for the page and can render and observe:

```
reticle_session({ action: "list" })
```

In a disconnected, hidden or throttled tab, a key press can be accepted while timers, rendering and later observation do not advance, so nothing you read back is trustworthy. Get a usable context first, for example a leased tab:

```
reticle_run({ tool: "reticle_lease", args: { action: "acquire", url } })
```

Pass `refuseWhenThrottled: true` on the action so a paused tab fails loudly instead of silently doing nothing. If you cannot get a healthy session, report the run as **blocked**, not passed, and do not keep retrying against it.

## List the controls

List every interactive control before touching the keyboard, or you will only test the ones you already knew about:

```
reticle_look({ sessionId, action: "page", mode: "interactive" })
```

The rest: https://github.com/reticlehq/reticle/blob/HEAD/skills/verify-keyboard-access/SKILL.md
