/** * Search Results Keyboard Navigation * Handles Arrow Up/Down, Enter, and Escape keys for accessible search * * This module enhances search inputs with keyboard navigation for WCAG compliance: * - Arrow Down: Navigate to next search result * - Arrow Up: Navigate to previous search result (or back to input) * - Enter: Select current result (navigate to link) * - Escape: Close search results and blur input * * Usage: * The script automatically initializes on DOMContentLoaded for all search inputs * with hx-target attribute pointing to a results container. * * HTMX Integration: * Results should include role="option" on each selectable item. * The script listens for htmx:afterSwap to reinitialize when results change. */ document.addEventListener('DOMContentLoaded', () => { const searchInputs = document.querySelectorAll('input[type="search"]'); searchInputs.forEach(input => { const resultsContainer = document.querySelector(input.getAttribute('hx-target')); if (!resultsContainer) return; let currentIndex = -1; input.addEventListener('keydown', (e) => { const results = resultsContainer.querySelectorAll('[role="option"]'); if (results.length === 0) return; switch(e.key) { case 'ArrowDown': e.preventDefault(); currentIndex = Math.min(currentIndex + 1, results.length - 1); updateSelection(results, currentIndex); break; case 'ArrowUp': e.preventDefault(); currentIndex = Math.max(currentIndex - 1, -1); if (currentIndex === -1) { input.focus(); } else { updateSelection(results, currentIndex); } break; case 'Enter': if (currentIndex >= 0) { e.preventDefault(); results[currentIndex].querySelector('a').click(); } break; case 'Escape': e.preventDefault(); resultsContainer.innerHTML = ''; input.blur(); break; } }); function updateSelection(results, index) { results.forEach((result, i) => { if (i === index) { result.setAttribute('aria-selected', 'true'); result.classList.add('bg-accent'); result.scrollIntoView({ block: 'nearest' }); } else { result.setAttribute('aria-selected', 'false'); result.classList.remove('bg-accent'); } }); } }); });