arayaroma/ether/a11y-focus-management

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Use when building a modal, dialog, or any UI that changes what's focused programmatically — moving focus in on open and restoring it on close. Use whenever a component conditionally renders/removes focusable content based on state (isOpen, route change).

Install

npx vskill@latest install arayaroma/ether/a11y-focus-management

SKILL.md

Focus management

Focus must move logically whenever the visible/interactive surface changes — most commonly modals, but also route transitions and any "reveal new interactive content" state change.

Modal: move focus in, restore focus out

tsx
export function Modal({ isOpen, onClose, title, children }: { isOpen: boolean; onClose: () => void; title: string; children: React.ReactNode }) {
const modalRef = useRef<HTMLDivElement>(null);
const previousFocusRef = useRef<HTMLElement | null>(null);
useEffect(() => {
if (isOpen) {
previousFocusRef.current = document.activeElement as HTMLElement;
modalRef.current?.focus();
} else {
previousFocusRef.current?.focus();
}
}, [isOpen]);
if (!isOpen) return null;
return (
<div ref={modalRef} role="dialog" aria-modal="true" aria-labelledby="modal-title" tabIndex={-1} onKeyDown={(e) => e.key === 'Escape' && onClose()}>
<h2 id="modal-title">{title}</h2>
{children}
<button onClick={onClose}>Close</button>
</div>
);
}

This covers initial focus and restoration only. It does not trap Tab/Shift+Tab inside the modal — without a trap, Tab can escape to page content behind the modal. For a real focus trap (handles nested portals, dynamically-added content), use a library like focus-trap-react rather than hand-rolling the Tab-cycling logic.

Checklist for any focus-moving component

  • [ ] Save document.activeElement before moving focus away from it
  • [ ] Move focus to a sensible target on open (the dialog container, or its first field — not left

on <body>)

  • [ ] Restore focus to the saved element on close, not just to <body>
  • [ ] role="dialog" + aria-modal="true" + aria-labelledby pointing at the visible title
  • [ ] Escape closes it
  • [ ] Tab is trapped inside while open (use a library for this part)

Related

  • a11y-semantics-aria — aria-expanded/aria-controls for non-modal disclosure widgets that

don't need focus trapping, just the ARIA state.

  • a11y-keyboard-nav — keyboard handling for widgets that live inside the modal (a listbox,

combobox, etc.), on top of the modal's own focus trap.

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

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
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/arayaroma/ether/a11y-focus-management/badge)](https://verified-skill.com/skills/arayaroma/ether/a11y-focus-management)

Something wrong with this skill or its page? Tell us.