Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion wata-board-frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ function Home() {
</form>
</div>

<footer className="mt-12 text-center text-xs text-slate-500">
<footer id="footer" className="mt-12 text-center text-xs text-slate-500">
<p>© {new Date().getFullYear()} Wata-Board. {t('app.footer.tagline')}</p>
</footer>
</div>
Expand Down
7 changes: 3 additions & 4 deletions wata-board-frontend/src/components/ResponsiveNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useState, useEffect, useRef } from 'react';
import { Link, useLocation } from 'react-router-dom';
import { NetworkSwitcher } from './NetworkSwitcher';
import MobileNavigation from './MobileNavigation';
import { announceToScreenReader, trapFocus, removeFocusTrap, generateId, getAriaLabel } from '../utils/accessibility';
import { announceToScreenReader, trapFocus, generateId, getAriaLabel } from '../utils/accessibility';

export const ResponsiveNavigation: React.FC = () => {
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
Expand All @@ -11,7 +11,6 @@ export const ResponsiveNavigation: React.FC = () => {
const menuButtonRef = useRef<HTMLButtonElement>(null);
const cleanupRef = useRef<(() => void) | null>(null);

const navigationId = useRef(generateId('navigation'));
const menuButtonId = useRef(generateId('menu-button'));

const isActive = (path: string) => {
Expand Down Expand Up @@ -95,7 +94,7 @@ export const ResponsiveNavigation: React.FC = () => {
className="border-b border-slate-800 bg-slate-900/60 backdrop-blur-sm sticky top-0 z-40"
role="navigation"
aria-label="Main navigation"
id={navigationId.current}
id="navigation"
>
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-16">
Expand Down Expand Up @@ -158,7 +157,7 @@ export const ResponsiveNavigation: React.FC = () => {
className="p-2 rounded-lg text-slate-400 hover:text-slate-200 hover:bg-slate-800 transition-colors focus:outline-none focus:ring-2 focus:ring-sky-500 focus:ring-offset-2 focus:ring-offset-slate-900"
aria-label={getAriaLabel('menu-button')}
aria-expanded={isMobileMenuOpen}
aria-controls={navigationId.current}
aria-controls="navigation"
id={menuButtonId.current}
>
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
Expand Down
56 changes: 34 additions & 22 deletions wata-board-frontend/src/components/SkipLinks.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,47 @@
import React from 'react';
import { Link } from 'react-router-dom';

interface SkipLinkProps {
href: string;
children: React.ReactNode;
className?: string;
}

export const SkipLink: React.FC<SkipLinkProps> = ({ href, children, className = '' }) => {
return (
<Link
to={href}
className={`skip-link ${className}`}
onClick={(e) => {
e.preventDefault();
const target = document.querySelector(href) as HTMLElement;
target?.focus();
target?.scrollIntoView({ behavior: 'smooth' });
}}
>
{children}
</Link>
);
const skipLinkClasses =
'absolute left-4 top-4 z-50 -translate-y-4 opacity-0 transition-all duration-200 ease-out focus:translate-y-0 focus:opacity-100 focus:z-50 focus:outline-none focus:ring-2 focus:ring-sky-500 focus:ring-offset-2 focus:ring-offset-slate-950 bg-slate-900 text-slate-100 px-3 py-2 rounded-md shadow-lg';

const handleSkipClick = (href: string) => (event: React.MouseEvent<HTMLAnchorElement>) => {
event.preventDefault();
const target = document.querySelector(href) as HTMLElement | null;
if (target) {
target.tabIndex = -1;
target.focus();
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
};

export const SkipLinks: React.FC = () => {
return (
<div className="sr-only">
<SkipLink href="#main-content">Skip to main content</SkipLink>
<SkipLink href="#navigation">Skip to navigation</SkipLink>
<SkipLink href="#payment-form">Skip to payment form</SkipLink>
</div>
<nav aria-label="Skip links">
<a
href="#main-content"
className={skipLinkClasses}
onClick={handleSkipClick('#main-content')}
>
Skip to main content
</a>
<a
href="#navigation"
className={skipLinkClasses}
onClick={handleSkipClick('#navigation')}
>
Skip to navigation
</a>
<a
href="#footer"
className={skipLinkClasses}
onClick={handleSkipClick('#footer')}
>
Skip to footer
</a>
</nav>
);
};
Loading