# Focus Management Patterns Reference guide for focus indicator styles, skip links, focus traps, and ARIA patterns. Used by focus-auditor and fix-implementer roles. ## Focus Indicator Style ### Recommended Pattern ```css /* Keyboard focus: visible outline */ :focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; } /* Mouse click: no outline */ :focus:not(:focus-visible) { outline: none; } ``` ### Requirements | Property | Minimum | Rationale | |----------|---------|-----------| | outline-width | 2px | Visibility at distance | | outline-style | solid | Consistent rendering | | outline-offset | 2px | Separation from element edge | | Contrast vs adjacent | >= 3:1 | WCAG 2.4.11 | ### Anti-Patterns (Do NOT) ```css /* BAD: Removes all focus indicators */ *:focus { outline: none; } /* BAD: Removes focus without alternative */ button:focus { outline: 0; } /* BAD: Only uses box-shadow (invisible in high contrast mode) */ :focus-visible { outline: none; box-shadow: 0 0 0 2px blue; } ``` ### Correct Alternative Indicator ```css /* If not using outline, MUST provide visible alternative */ :focus-visible { outline: 2px solid transparent; /* For Windows high contrast mode */ box-shadow: 0 0 0 2px var(--color-accent); } ``` ## Skip Link ### HTML ```html
``` ### CSS ```css .skip-link { position: absolute; left: -9999px; top: auto; width: 1px; height: 1px; overflow: hidden; z-index: -1; } .skip-link:focus { position: fixed; left: 16px; top: 16px; width: auto; height: auto; overflow: visible; z-index: 9999; background: var(--color-paper, #fff); color: var(--color-ink, #000); padding: 8px 16px; border: 2px solid var(--color-ink, #000); border-radius: 4px; font-size: 1rem; text-decoration: underline; } ``` ### Requirements | Check | Requirement | |-------|-------------| | Position | First focusable element in DOM | | Default state | Visually hidden (not display:none or visibility:hidden) | | Focus state | Visible, fixed position, high z-index | | Target | Points to main content area with valid ID | | Contrast | Link text meets 4.5:1 contrast against background | ## Focus Trap (Modals/Dialogs) ### Implementation Pattern ```javascript function trapFocus(dialog) { const focusableSelector = [ 'a[href]', 'button:not([disabled])', 'input:not([disabled])', 'select:not([disabled])', 'textarea:not([disabled])', '[tabindex]:not([tabindex="-1"])' ].join(', '); const focusableElements = dialog.querySelectorAll(focusableSelector); const firstFocusable = focusableElements[0]; const lastFocusable = focusableElements[focusableElements.length - 1]; // Store trigger for focus restore const trigger = document.activeElement; // Move focus to first element firstFocusable.focus(); dialog.addEventListener('keydown', function(e) { if (e.key === 'Escape') { closeDialog(dialog); trigger.focus(); // Restore focus return; } if (e.key !== 'Tab') return; if (e.shiftKey) { // Shift+Tab at first element -> wrap to last if (document.activeElement === firstFocusable) { e.preventDefault(); lastFocusable.focus(); } } else { // Tab at last element -> wrap to first if (document.activeElement === lastFocusable) { e.preventDefault(); firstFocusable.focus(); } } }); } ``` ### Dialog HTML Pattern ```html

Dialog Title

``` ### Requirements | Step | Action | Detail | |------|--------|--------| | Open | Store trigger | `const trigger = document.activeElement` | | Open | Move focus | Focus first focusable element in dialog | | Open | Lock background | `document.body.style.overflow = 'hidden'` or `inert` attribute | | Open | Set ARIA | `aria-modal="true"` on dialog | | Tab | Cycle within | Tab/Shift+Tab wrap within dialog focusable elements | | Escape | Close + restore | Close dialog, restore focus to trigger | | Close | Unlock background | Remove scroll lock / inert | ## ARIA Patterns ### Button Patterns ```html ``` ### Dialog Pattern ```html

Confirm Action

Are you sure you want to proceed?

``` ### Live Region Patterns ```html
3 items in cart
Email address is invalid
``` ### Navigation Pattern ```html ``` ### Tab Pattern ```html
General settings content
``` **Keyboard**: Arrow Left/Right to switch tabs, Tab to move into panel content. ### Form Error Pattern ```html ``` ## Keyboard Navigation Reference | Component | Key | Action | |-----------|-----|--------| | Link | Enter | Activate | | Button | Enter, Space | Activate | | Checkbox | Space | Toggle | | Radio group | Arrow Up/Down | Select previous/next | | Tab list | Arrow Left/Right | Switch tab | | Menu | Arrow Up/Down | Navigate items | | Menu | Enter | Select item | | Menu | Escape | Close menu | | Dialog | Escape | Close dialog | | Slider | Arrow Left/Right | Decrease/increase | | Combobox | Arrow Down | Open dropdown | | Combobox | Enter | Select highlighted | | Combobox | Escape | Close dropdown | | Tree | Arrow Up/Down | Navigate siblings | | Tree | Arrow Right | Expand / enter child | | Tree | Arrow Left | Collapse / go to parent | ## Target Size Reference | Standard | Minimum Size | Notes | |----------|-------------|-------| | WCAG 2.5.8 (AA) | 24x24px CSS pixels | Or adequate spacing between targets | | WCAG 2.5.5 (AAA) | 44x44px CSS pixels | Recommended for touch interfaces | | Apple HIG | 44x44pt | iOS touch targets | | Material Design | 48x48dp | Android touch targets | **Exceptions**: Inline links within text, browser default controls, essential fixed-size elements.