Lightweight (≤2kB) focus trapping utility for implementing accessible keyboard navigation constraints in modal dialogs, sidebars, and other contained UI components.
- ✅ Full WAI-ARIA compliance for accessibility
- ✅ Tiny footprint (ES6 module)
- ✅ Zero dependencies
- ✅ Flexible focus control
- ✅ Automatic cleanup
- ✅ TypeScript support
npm install focus-trap-liteimport { initFocusTrap } from 'focus-trap-lite'
// Initialize trap on modal open
function openModal() {
const trap = initFocusTrap(modalElement)
// Add your modal opening logic
// Clean up manually if needed
// trap.destroy()
}
// Trap automatically cleans up when:
// - User closes modal (Escape key)
// - Focus escapes trap boundaries (if logic allows)
// - Component unmounts// Container element with custom selector and options
initFocusTrap(document.querySelector('#modal-container'), '.custom-focusable', {
focus: true, // Auto-focus on initialization
firstFocusableElement: '#first-input', // Specific start element
})
// Nested Modals support
// The library maintains a stack of active traps.
// When a new trap is initialized, it takes precedence.
// When destroyed (e.g. via Escape), focus control returns to the previous trap.| Parameter | Type | Description |
|---|---|---|
| element | Element |
(Optional) DOM element to scope the focus trap. When omitted, uses document.body |
| selector | string |
(Optional) CSS selector for focusable elements within container. Default: standard focusable elements |
| options | Object |
(Optional) Configuration object |
| options.focus | boolean |
Auto-focus the first element on initialization. Default: false |
| options.firstFocusableElement | HTMLElement|string |
Element to focus initially. Can be a selector string or DOM element. |
Returns:
{
destroy: () => void, // Manually destroy the trap
container: HTMLElement // The container element
}Behavior:
- Creates keyboard navigation constraints (Tab / Shift+Tab)
- Handles boundary focus wrapping
- Nested Traps: Supports multiple stacked traps (LIFO)
- Auto Cleanup:
- On
Escapekey press - When calling
destroy() - When calling function returns (if implicit, though manual destroy is recommended for SPAs)
- On
- Smart Filtering: Ignores hidden, invisible, or
tabindex="-1"elements
- Breaking Change:
initFocusTrapnow returns an object{ destroy, container }instead ofvoid. - New Feature: Added
optionsparameter.options.focus: Auto-focus support.options.firstFocusableElement: Custom initial focus target.
- New Feature: Nested focus traps support (Stack-based).
- New Feature:
Escapekey support for closing the trap. - Improvement: Better filtering of non-focusable elements (hidden, zero-size,
tabindex="-1"). - Improvement: Optimized internal logic and variable naming.
Modern browsers with ES6 support:
Chrome |
Firefox |
Safari |
|---|---|---|
| 88+ | 78+ | 14.1+ |
For legacy browser support, add Array.prototype.at() polyfill.
- Fork the repository
- Clone your fork
git clone https://github.com/your-username/focus-trap-lite.git- Install dependencies
npm install- Create feature branch
git checkout -b feature/your-feature- Commit changes
- Push to branch
- Create Pull Request
MIT © Pipecraft
📝 Report issues on GitHub