focus-management

Essential patterns for managing keyboard focus in accessible web applications, ensuring keyboard-only users can navigate complex interactive components.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "focus-management" with this command: npx skills add yonatangross/orchestkit/yonatangross-orchestkit-focus-management

Focus Management

Essential patterns for managing keyboard focus in accessible web applications, ensuring keyboard-only users can navigate complex interactive components.

Overview

  • Building modals, dialogs, or drawers that require focus trapping

  • Implementing tab panels, menus, or toolbars with roving tabindex

  • Restoring focus after closing overlays or completing actions

  • Creating skip links for keyboard navigation

  • Ensuring focus visibility meets WCAG 2.4.7 requirements

Quick Reference

FocusScope Trap (React Aria)

import { FocusTrap } from '@react-aria/focus';

function Modal({ isOpen, onClose, children }) { if (!isOpen) return null; return ( <div role="dialog" aria-modal="true"> <FocusTrap> <div className="modal-content"> {children} <button onClick={onClose}>Close</button> </div> </FocusTrap> </div> ); }

Roving Tabindex

function TabList({ tabs, onSelect }) { const [activeIndex, setActiveIndex] = useState(0); const tabRefs = useRef<HTMLButtonElement[]>([]);

const handleKeyDown = (e: KeyboardEvent, index: number) => { const keyMap: Record<string, number> = { ArrowRight: (index + 1) % tabs.length, ArrowLeft: (index - 1 + tabs.length) % tabs.length, Home: 0, End: tabs.length - 1, }; if (e.key in keyMap) { e.preventDefault(); setActiveIndex(keyMap[e.key]); tabRefs.current[keyMap[e.key]]?.focus(); } };

return ( <div role="tablist"> {tabs.map((tab, i) => ( <button key={tab.id} ref={(el) => (tabRefs.current[i] = el!)} role="tab" tabIndex={i === activeIndex ? 0 : -1} aria-selected={i === activeIndex} onKeyDown={(e) => handleKeyDown(e, i)} onClick={() => { setActiveIndex(i); onSelect(tab); }}> {tab.label} </button> ))} </div> ); }

Focus Restore

function useRestoreFocus(isOpen: boolean) { const triggerRef = useRef<HTMLElement | null>(null);

useEffect(() => { if (isOpen) { triggerRef.current = document.activeElement as HTMLElement; } else if (triggerRef.current) { triggerRef.current.focus(); triggerRef.current = null; } }, [isOpen]); }

Key Decisions

Decision Option A Option B Recommendation

Trap vs Contain FocusTrap (strict) FocusScope (soft) Trap for modals, Scope for popovers

Restore strategy Trigger element Last focused Always restore to trigger

Skip links Single main skip Multiple landmarks Multiple for complex layouts

Initial focus First focusable Auto-focus input Context-dependent, prefer inputs

Focus visible Browser default Custom outline Custom :focus-visible for consistency

Anti-Patterns (FORBIDDEN)

// NEVER use positive tabindex - breaks natural tab order <button tabIndex={5}>Bad</button>

// NEVER remove focus outline without replacement (WCAG 2.4.7) button:focus { outline: none; }

// NEVER trap focus without Escape key handler <FocusTrap><div>No way out!</div></FocusTrap>

// NEVER auto-focus without user expectation useEffect(() => inputRef.current?.focus(), []);

// NEVER hide skip links permanently - must be visible on focus .skip-link { display: none; }

Related Skills

  • a11y-testing

  • Test focus management with Playwright and axe

  • design-system-starter

  • Focus indicators in design tokens

  • motion-animation-patterns

  • Animate focus transitions accessibly

Capability Details

focus-trap

Keywords: focus trap, modal focus, dialog focus, FocusTrap, aria-modal Solves:

  • Trap focus within modals and dialogs

  • Prevent focus escaping to background content

  • Handle Escape key to close and release trap

roving-tabindex

Keywords: roving tabindex, arrow navigation, tablist, menu keyboard Solves:

  • Navigate widget items with arrow keys

  • Maintain single tab stop for composite widgets

  • Support Home/End for quick navigation

focus-restore

Keywords: focus restore, return focus, trigger focus, focus memory Solves:

  • Return focus to trigger after closing overlay

  • Preserve focus context across interactions

  • Handle focus when elements are removed from DOM

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

responsive-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
General

domain-driven-design

No summary provided by upstream source.

Repository SourceNeeds Review
General

dashboard-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
General

memory

No summary provided by upstream source.

Repository SourceNeeds Review