2026-01-15 11:31:09 +01:00

84 lines
2.6 KiB
TypeScript

import { useEffect, useState } from 'react'
import { t } from '@/lib/i18n'
interface SkipLink {
id: string
label: string
targetId: string
}
const SKIP_LINKS: SkipLink[] = [
{ id: 'skip-main', label: 'navigation.skipToMain', targetId: 'main-content' },
{ id: 'skip-filters', label: 'navigation.skipToFilters', targetId: 'filters-section' },
{ id: 'skip-articles', label: 'navigation.skipToArticles', targetId: 'articles-section' },
]
export function SkipLinks(): React.ReactElement | null {
const [isVisible, setIsVisible] = useState(false)
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent): void => {
if (e.key === 'Tab' && !e.shiftKey) {
setIsVisible(true)
}
}
const handleKeyUp = (e: KeyboardEvent): void => {
if (e.key === 'Tab' && !e.shiftKey) {
setIsVisible(true)
}
}
const handleClick = (): void => {
setIsVisible(false)
}
document.addEventListener('keydown', handleKeyDown)
document.addEventListener('keyup', handleKeyUp)
document.addEventListener('click', handleClick, true)
document.addEventListener('mousedown', handleClick, true)
return () => {
document.removeEventListener('keydown', handleKeyDown)
document.removeEventListener('keyup', handleKeyUp)
document.removeEventListener('click', handleClick, true)
document.removeEventListener('mousedown', handleClick, true)
}
}, [])
if (!isVisible) {
return null
}
return (
<div className="sr-only focus-within:not-sr-only focus-within:absolute focus-within:z-50 focus-within:top-4 focus-within:left-4">
<nav aria-label={t('navigation.skipLinks')} className="flex flex-col gap-2">
{SKIP_LINKS.map((link) => (
<SkipLinkItem key={link.id} link={link} onFocus={() => setIsVisible(true)} />
))}
</nav>
</div>
)
}
function SkipLinkItem({ link, onFocus }: { link: SkipLink; onFocus: () => void }): React.ReactElement {
const handleClick = (e: React.MouseEvent<HTMLAnchorElement>): void => {
e.preventDefault()
const target = document.getElementById(link.targetId)
if (target) {
target.focus()
target.scrollIntoView({ behavior: 'smooth', block: 'start' })
}
}
return (
<a
href={`#${link.targetId}`}
onClick={handleClick}
onFocus={onFocus}
className="bg-neon-cyan text-cyber-darker px-4 py-2 rounded-lg font-semibold focus:outline-none focus:ring-2 focus:ring-neon-green focus:ring-offset-2 focus:ring-offset-cyber-darker"
aria-label={t(link.label)}
>
{t(link.label)}
</a>
)
}