/** * Accessibility Enhancements * WCAG 2.1 AA Compliance Utilities */ (function () { "use strict"; class AccessibilityManager { constructor() { this.init(); } init() { this.addSkipLinks(); this.enhanceFocusManagement(); this.addARIALabels(); this.setupKeyboardNavigation(); this.announceChanges(); } // Add skip link to main content addSkipLinks() { if (document.querySelector(".skip-link")) return; const skipLink = document.createElement("a"); skipLink.href = "#main-content"; skipLink.className = "skip-link"; skipLink.textContent = "Skip to main content"; skipLink.addEventListener("click", (e) => { e.preventDefault(); const main = document.getElementById("main-content") || document.querySelector("main"); if (main) { main.setAttribute("tabindex", "-1"); main.focus(); main.removeAttribute("tabindex"); } }); document.body.insertBefore(skipLink, document.body.firstChild); } // Enhance focus management enhanceFocusManagement() { // Track focus for modal/dropdown management let lastFocusedElement = null; document.addEventListener("focusin", (e) => { const dropdown = e.target.closest( '[role="dialog"], .cart-dropdown, .wishlist-dropdown' ); if (dropdown && dropdown.classList.contains("active")) { if (!lastFocusedElement) { lastFocusedElement = document.activeElement; } } }); // Return focus when dropdowns close const observeDropdowns = () => { const dropdowns = document.querySelectorAll( ".cart-dropdown, .wishlist-dropdown" ); dropdowns.forEach((dropdown) => { const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.attributeName === "class") { if ( !dropdown.classList.contains("active") && lastFocusedElement ) { lastFocusedElement.focus(); lastFocusedElement = null; } } }); }); observer.observe(dropdown, { attributes: true }); }); }; if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", observeDropdowns); } else { observeDropdowns(); } } // Add missing ARIA labels addARIALabels() { // Add labels to buttons without text document .querySelectorAll("button:not([aria-label]):not([aria-labelledby])") .forEach((button) => { if (button.textContent.trim() === "") { const icon = button.querySelector('i[class*="bi-"]'); if (icon) { const iconClass = Array.from(icon.classList).find((c) => c.startsWith("bi-") ); if (iconClass) { const label = iconClass.replace("bi-", "").replace(/-/g, " "); button.setAttribute("aria-label", label); } } } }); // Ensure all images have alt text document.querySelectorAll("img:not([alt])").forEach((img) => { img.setAttribute("alt", ""); }); // Add role to navigation landmarks document.querySelectorAll(".navbar, .modern-navbar").forEach((nav) => { if (!nav.getAttribute("role")) { nav.setAttribute("role", "navigation"); } }); } // Setup keyboard navigation setupKeyboardNavigation() { // Escape key closes dropdowns document.addEventListener("keydown", (e) => { if (e.key === "Escape") { const activeDropdown = document.querySelector( ".cart-dropdown.active, .wishlist-dropdown.active" ); if (activeDropdown) { const closeBtn = activeDropdown.querySelector( ".close-btn, [data-close]" ); if (closeBtn) closeBtn.click(); } } }); // Tab trap in modal/dropdowns document .querySelectorAll(".cart-dropdown, .wishlist-dropdown") .forEach((dropdown) => { dropdown.addEventListener("keydown", (e) => { if (e.key === "Tab" && dropdown.classList.contains("active")) { const focusableElements = dropdown.querySelectorAll( 'a[href], button:not([disabled]), input:not([disabled]), [tabindex]:not([tabindex="-1"])' ); const firstElement = focusableElements[0]; const lastElement = focusableElements[focusableElements.length - 1]; if (e.shiftKey && document.activeElement === firstElement) { e.preventDefault(); lastElement.focus(); } else if ( !e.shiftKey && document.activeElement === lastElement ) { e.preventDefault(); firstElement.focus(); } } }); }); } // Announce dynamic changes to screen readers announceChanges() { // Create live region if it doesn't exist let liveRegion = document.getElementById("aria-live-region"); if (!liveRegion) { liveRegion = document.createElement("div"); liveRegion.id = "aria-live-region"; liveRegion.setAttribute("role", "status"); liveRegion.setAttribute("aria-live", "polite"); liveRegion.setAttribute("aria-atomic", "true"); liveRegion.className = "sr-only"; document.body.appendChild(liveRegion); } // Listen for cart/wishlist updates window.addEventListener("cart-updated", () => { const count = window.AppState?.getCartCount?.() || 0; this.announce( `Cart updated. ${count} item${count !== 1 ? "s" : ""} in cart.` ); }); window.addEventListener("wishlist-updated", () => { const count = window.AppState?.wishlist?.length || 0; this.announce( `Wishlist updated. ${count} item${ count !== 1 ? "s" : "" } in wishlist.` ); }); } // Announce message to screen readers announce(message) { const liveRegion = document.getElementById("aria-live-region"); if (liveRegion) { liveRegion.textContent = ""; setTimeout(() => { liveRegion.textContent = message; }, 100); } } } // Initialize accessibility manager if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", () => { window.A11y = new AccessibilityManager(); }); } else { window.A11y = new AccessibilityManager(); } })();