Focus management in SPAs is one of the most commonly broken aspects of keyboard accessibility. When routes change, focus stays where it was. When modals open, focus doesn't move into them. When modals close, focus doesn't return to the trigger. Each of these is a real barrier for keyboard and screen reader users.
The Library
Three utilities, each solving one problem:
trapFocus(container) — confines Tab and Shift+Tab to the given container. Used for modals, drawers, and menus. Returns a cleanup function.
const cleanup = trapFocus(modalElement);
// When modal closes:
cleanup();
restoreFocus(trigger) — saves the currently focused element and returns a function that restores focus to it. Used when opening any overlay.
announceTo(message, priority) — injects a message into an ARIA live region. Used for route changes, form submissions, and dynamic content updates.
Design Decisions
The library has no dependencies. It is 2KB uncompressed. It works with any framework — React, Vue, Svelte, plain DOM. It does not manage state; it manages focus.
Framework-specific wrappers (React hooks) are provided separately so the core stays portable.
Usage in Production
The library is used in three production applications. Since adoption, keyboard navigation audit findings related to focus management have been consistently zero across all three.