Lightweight, drop-in JavaScript library for accessible UI patterns
Version: 0.1.0 (POC) Status: Proof of Concept License: MIT
focus-patterns is a zero-dependency JavaScript library that provides accessible UI patterns with automatic keyboard handling and ARIA semantics. Think "Bootstrap for accessibility" - developers add markup, get fully accessible components.
This is a Proof of Concept demonstrating the Tabs pattern implementation.
- ✅ Tabs Component with full ARIA support
- ✅ Keyboard Navigation (Arrow keys, Home, End)
- ✅ Manual & Auto Activation modes
- ✅ Horizontal & Vertical orientations
- ✅ Forgiving Mode - auto-fixes missing ARIA attributes
- ✅ Strict Mode - enforces valid markup
- ✅ Intelligent tabindex - auto-detects focusable content
- ✅ Auto-initialization from HTML markup
- ✅ Screen Reader Support with announcements
- ✅ Zero CSS - style it however you want
- ✅ TypeScript with full type definitions
npm installnpx tscThere are two ways to test the demo:
Option A: Open Directly (simplest)
# macOS
open demo/index.html
# Linux
xdg-open demo/index.html
# Windows
start demo/index.htmlOption B: Use a Local Server (recommended)
# Using Python 3
python3 -m http.server 8000
# Using Node's http-server
npx http-server
# Then visit: http://localhost:8000/demo/Why use a server? Module scripts require CORS headers, which file:// URLs don't provide. A local server ensures proper module loading.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My Page</title>
<script src="dist/index.js"></script>
</head>
<body>
<div data-focus="tabs" data-activation="manual">
<div data-role="tablist" aria-label="My Tabs">
<button data-role="tab" data-controls="panel1">Tab 1</button>
<button data-role="tab" data-controls="panel2">Tab 2</button>
</div>
<div data-role="tabpanel" id="panel1">
<h2>Panel 1</h2>
<p>Content for first tab</p>
</div>
<div data-role="tabpanel" id="panel2">
<h2>Panel 2</h2>
<p>Content for second tab</p>
</div>
</div>
</body>
</html>// Access via global object
const tabs = window.FocusPatterns.Tabs.init(element, {
activation: 'manual',
orientation: 'horizontal',
onChange: (index, tab) => {
console.log(`Switched to tab ${index}`);
}
});
// Control programmatically
tabs.selectTab(1);Automatically fixes missing ARIA attributes. Perfect for rapid prototyping:
<!-- Minimal markup - library adds all ARIA attributes -->
<div data-focus="tabs">
<div data-role="tablist" aria-label="Settings">
<button data-role="tab">Tab 1</button>
<button data-role="tab">Tab 2</button>
</div>
<div data-role="tabpanel">Content 1</div>
<div data-role="tabpanel">Content 2</div>
</div>What it auto-fixes:
- ✅ Generates IDs if missing
- ✅ Adds
role="tab"androle="tabpanel" - ✅ Sets up
aria-controlsandaria-labelledby - ✅ Intelligently adds
tabindex(only if panel has no focusable content) - ✅ Sets initial
aria-selectedstates
Enforces valid markup. Best for production code:
<div data-focus="tabs" data-strict="true">
<div role="tablist" aria-label="Settings">
<button role="tab" id="tab-1" aria-controls="panel-1">Tab 1</button>
<button role="tab" id="tab-2" aria-controls="panel-2">Tab 2</button>
</div>
<div role="tabpanel" id="panel-1" aria-labelledby="tab-1">Content 1</div>
<div role="tabpanel" id="panel-2" aria-labelledby="tab-2">Content 2</div>
</div>Throws errors if missing:
- ❌ Missing
roleattributes - ❌ Missing IDs on tabs or panels
- ❌ Missing
aria-controlsoraria-labelledby
| Option | Type | Default | Description |
|---|---|---|---|
strict |
boolean |
false |
Enable strict mode (requires valid markup) |
activation |
'auto' | 'manual' |
'manual' |
Auto-activate on arrow key or require Enter/Space |
orientation |
'horizontal' | 'vertical' |
'horizontal' |
Tab list orientation |
loop |
'true' | 'false' |
'true' |
Whether arrow keys wrap around |
onChange |
function |
- | Callback when tab changes |
idPrefix |
string |
'tab' |
Prefix for auto-generated IDs (forgiving mode only) |
announceChanges |
boolean |
true |
Announce tab changes to screen readers |
| Key | Action |
|---|---|
Tab |
Move focus into/out of tab list |
Left/Right Arrow (horizontal) |
Navigate between tabs |
Up/Down Arrow (vertical) |
Navigate between tabs |
Home |
Jump to first tab |
End |
Jump to last tab |
Enter/Space (manual mode) |
Activate focused tab |
- ✅ WCAG 2.1 AA Compliant
- ✅ Full ARIA Implementation (WAI-ARIA 1.2)
- ✅ Keyboard Navigation with all standard keys
- ✅ Screen Reader Support tested with NVDA, JAWS, VoiceOver
- ✅ Focus Management with visible focus indicators
- ✅ Reduced Motion support
- ✅ High Contrast mode support
focus-patterns/
├── src/
│ ├── core/
│ │ └── utils.ts # Utility functions
│ ├── patterns/
│ │ └── tabs/
│ │ └── tabs.ts # Tabs implementation
│ └── index.ts # Main entry point
├── dist/ # Compiled JavaScript
├── demo/
│ ├── index.html # Demo page
│ └── examples/
│ └── minimal.css # Example styles
├── package.json
├── tsconfig.json
└── README.md
This POC demonstrates the core concept. Next steps per the PRD:
- ⏭️ Add bundling (Rollup/Vite) for UMD/ESM/CJS formats
- ⏭️ Add automated testing (Playwright)
- ⏭️ Add more patterns (Accordion, Disclosure, Modal)
- ⏭️ Publish to npm
- ⏭️ Create documentation website
npm run buildnpm run devnpm run demo- Chrome/Edge 90+
- Firefox 88+
- Safari 14+
- Mobile browsers (iOS 14+, Android Chrome 90+)
MIT © Hein
Built with ❤️ for accessibility
🎯 Mission: Make the web accessible to everyone, one pattern at a time.