️(frontend) skip link as anchor instead of button

Replace button with anchor link
This commit is contained in:
Cyril
2026-03-16 13:30:59 +01:00
parent da79c310ae
commit bf93640af8
3 changed files with 14 additions and 45 deletions
+1 -1
View File
@@ -11,6 +11,7 @@ and this project adheres to
- 💫(frontend) fix the help button to the bottom in tree #2073
- ♿️(frontend) improve version history list accessibility #2033
- ♿️(frontend) fix more options menu feedback for screen readers #2071
- ♿(frontend) focus skip link on headings and skip grid dropzone #1983
## [v4.8.2] - 2026-03-19
@@ -68,7 +69,6 @@ and this project adheres to
- ⬆️(frontend) upgrade Next.js to v16 #1980
- ♿️(frontend) fix aria-label and landmark on document banner state #1986
- 🌐(i18n) add "new window" translation key for waffle aria-label #1984
- ♿(frontend) focus skip link on headings and skip grid dropzone #1983
### Fixed
@@ -196,18 +196,18 @@ test.describe('Header: Skip to Content', () => {
}) => {
await page.goto('/');
// Wait for skip button to be mounted (client-side only component)
const skipButton = page.getByRole('button', { name: 'Go to content' });
await skipButton.waitFor({ state: 'attached' });
// Wait for skip link to be mounted (client-side only component)
const skipLink = page.getByRole('link', { name: 'Go to content' });
await skipLink.waitFor({ state: 'attached' });
// First TAB shows the skip button
// First TAB shows the skip link
await page.keyboard.press('Tab');
// The skip button should be visible and focused
await expect(skipButton).toBeFocused();
await expect(skipButton).toBeVisible();
// The skip link should be visible and focused
await expect(skipLink).toBeFocused();
await expect(skipLink).toBeVisible();
// Clicking moves focus to the page heading
await skipButton.click();
await skipLink.click();
const pageHeading = page.getByRole('heading', {
name: 'All docs',
level: 2,
@@ -1,38 +1,17 @@
import { Button } from '@gouvfr-lasuite/cunningham-react';
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { css } from 'styled-components';
import { Box } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
import { MAIN_LAYOUT_ID } from '@/layouts/conf';
import { focusMainContentStart } from '@/layouts/utils';
export const SkipToContent = () => {
const { t } = useTranslation();
const router = useRouter();
const { spacingsTokens } = useCunninghamTheme();
const [isVisible, setIsVisible] = useState(false);
// Reset focus after route change so first TAB goes to skip link
useEffect(() => {
const handleRouteChange = () => {
(document.activeElement as HTMLElement)?.blur();
document.body.setAttribute('tabindex', '-1');
document.body.focus({ preventScroll: true });
setTimeout(() => {
document.body.removeAttribute('tabindex');
}, 100);
};
router.events.on('routeChangeComplete', handleRouteChange);
return () => {
router.events.off('routeChangeComplete', handleRouteChange);
};
}, [router.events]);
const handleClick = (e: React.MouseEvent) => {
e.preventDefault();
const focusTarget = focusMainContentStart();
@@ -43,21 +22,12 @@ export const SkipToContent = () => {
};
return (
<Box
$css={css`
.c__button--brand--primary.--docs--skip-to-content:focus-visible {
box-shadow:
0 0 0 1px var(--c--globals--colors--white-000),
0 0 0 4px var(--c--contextuals--border--semantic--brand--primary);
border-radius: var(--c--globals--spacings--st);
}
`}
>
<Box>
<Button
onClick={handleClick}
type="button"
href={`#${MAIN_LAYOUT_ID}`}
color="brand"
className="--docs--skip-to-content"
onClick={handleClick}
onFocus={() => setIsVisible(true)}
onBlur={() => setIsVisible(false)}
style={{
@@ -65,7 +35,6 @@ export const SkipToContent = () => {
pointerEvents: isVisible ? 'auto' : 'none',
position: 'fixed',
top: spacingsTokens['2xs'],
// padding header + logo(32px) + gap(3xs≈4px) + text "Docs"(≈70px) + 12px
left: `calc(${spacingsTokens['base']} + 32px + ${spacingsTokens['3xs']} + 70px + 12px)`,
zIndex: 9999,
whiteSpace: 'nowrap',