import { useEffect, useCallback } from 'react'; // Skip to content link for keyboard navigation export function SkipToContent() { const handleClick = useCallback((e: React.MouseEvent) => { e.preventDefault(); const mainContent = document.getElementById('main-content'); if (mainContent) { mainContent.focus(); mainContent.scrollIntoView({ behavior: 'smooth' }); } }, []); return ( Skip to content ); } // Announce page changes to screen readers export function usePageAnnounce() { useEffect(() => { const mainContent = document.getElementById('main-content'); if (mainContent) { // Set aria-live region mainContent.setAttribute('aria-live', 'polite'); mainContent.setAttribute('aria-atomic', 'true'); } }, []); } // Focus trap for modals export function useFocusTrap(isActive: boolean, containerRef: React.RefObject) { useEffect(() => { if (!isActive || !containerRef.current) return; const container = containerRef.current; const focusableElements = container.querySelectorAll( 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])' ); const firstElement = focusableElements[0]; const lastElement = focusableElements[focusableElements.length - 1]; const handleKeyDown = (e: KeyboardEvent) => { if (e.key !== 'Tab') return; if (e.shiftKey && document.activeElement === firstElement) { e.preventDefault(); lastElement?.focus(); } else if (!e.shiftKey && document.activeElement === lastElement) { e.preventDefault(); firstElement?.focus(); } }; // Focus first element when trap is activated firstElement?.focus(); container.addEventListener('keydown', handleKeyDown); return () => container.removeEventListener('keydown', handleKeyDown); }, [isActive, containerRef]); } // Manage focus visibility export function useFocusVisible() { useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === 'Tab') { document.body.classList.add('focus-visible'); } }; const handleMouseDown = () => { document.body.classList.remove('focus-visible'); }; document.addEventListener('keydown', handleKeyDown); document.addEventListener('mousedown', handleMouseDown); return () => { document.removeEventListener('keydown', handleKeyDown); document.removeEventListener('mousedown', handleMouseDown); }; }, []); } // Announce messages to screen readers export function announce(message: string, priority: 'polite' | 'assertive' = 'polite') { const announcement = document.createElement('div'); announcement.setAttribute('role', 'status'); announcement.setAttribute('aria-live', priority); announcement.setAttribute('aria-atomic', 'true'); announcement.className = 'sr-only'; announcement.textContent = message; document.body.appendChild(announcement); // Remove after announcement setTimeout(() => { document.body.removeChild(announcement); }, 1000); } // Language switcher component import { useTranslation } from 'react-i18next'; import { Button } from '@/components/ui/button'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import { Globe } from 'lucide-react'; const languages = [ { code: 'en', name: 'English', flag: '🇬🇧' }, { code: 'it', name: 'Italiano', flag: '🇮🇹' }, ]; export function LanguageSwitcher() { const { i18n } = useTranslation(); const currentLang = languages.find((l) => l.code === i18n.language) || languages[0]; const changeLanguage = (code: string) => { i18n.changeLanguage(code); }; return ( {languages.map((lang) => ( changeLanguage(lang.code)} className={i18n.language === lang.code ? 'bg-accent' : ''} > {lang.name} ))} ); }