mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-24 15:31:09 -05:00
Add secret management guide, client-side performance monitoring, and search accessibility enhancements
- Introduced a comprehensive Secret Management Guide detailing best practices, secret classification, development setup, production management, rotation procedures, and emergency protocols. - Implemented a client-side performance monitoring script to track various metrics including page load performance, paint metrics, layout shifts, and memory usage. - Enhanced search accessibility with keyboard navigation support for search results, ensuring compliance with WCAG standards and improving user experience.
This commit is contained in:
75
backend/static/js/search-accessibility.js
Normal file
75
backend/static/js/search-accessibility.js
Normal file
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* 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');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user