From 8d2dd5b565396c42e3526bd71e0c4fc20acc6393 Mon Sep 17 00:00:00 2001 From: Cyril Date: Wed, 8 Jul 2026 11:21:06 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8(frontend)=20restore=20skip=20to=20con?= =?UTF-8?q?tent=20link=20after=20header=20redesign?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-add skip link with responsive positioning and document title focus. --- CHANGELOG.md | 5 +- .../e2e/__tests__/app-impress/header.spec.ts | 5 +- .../impress/src/components/SkipToContent.tsx | 48 ++++++++++++++++--- .../apps/impress/src/layouts/utils.ts | 5 ++ src/frontend/apps/impress/src/pages/_app.tsx | 6 ++- 5 files changed, 57 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b75d0990d..2aafeb4ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to ## [Unreleased] +### Added + +- ♿️(frontend) restore skip to content link after header redesign #2510 + ## [v5.4.1] - 2026-07-09 ### Changed @@ -17,7 +21,6 @@ and this project adheres to - 💄(frontend) fix some UI/UX in the left panel #2516 - 🐛(frontend) fix tree dnd firefox #2516 - ## [v5.4.0] - 2026-07-07 ### Added diff --git a/src/frontend/apps/e2e/__tests__/app-impress/header.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/header.spec.ts index 6204abe3f..34c6b6907 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/header.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/header.spec.ts @@ -1,10 +1,7 @@ import { expect, test } from '@playwright/test'; test.describe('Header', () => { - /** - * TODO: add back the skip link - */ - test.skip('it displays skip link on first TAB and focuses page heading on click', async ({ + test('it displays skip link on first TAB and focuses page heading on click', async ({ page, }) => { await page.goto('/'); diff --git a/src/frontend/apps/impress/src/components/SkipToContent.tsx b/src/frontend/apps/impress/src/components/SkipToContent.tsx index 1ba63c9fd..5a95a1486 100644 --- a/src/frontend/apps/impress/src/components/SkipToContent.tsx +++ b/src/frontend/apps/impress/src/components/SkipToContent.tsx @@ -1,31 +1,66 @@ import { Button } from '@gouvfr-lasuite/cunningham-react'; +import { useRouter } from 'next/router'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { Box } from '@/components'; +import { Box } from '@/components/Box'; import { useCunninghamTheme } from '@/cunningham'; +import { useLeftPanelStore } from '@/features/left-panel/stores/useLeftPanelStore'; import { MAIN_LAYOUT_ID } from '@/layouts/conf'; import { focusMainContentStart } from '@/layouts/utils'; +import { useResponsiveStore } from '@/stores'; + +const LEFT_PANEL_CLOSE_TRANSITION_MS = 200; +const HEADER_ROW_HEIGHT = '68px'; +const HEADER_ICON_WIDTH = '32px'; +const COLLAPSE_BUTTON_WIDTH = '48px'; export const SkipToContent = () => { const { t } = useTranslation(); + const { pathname } = useRouter(); const { spacingsTokens } = useCunninghamTheme(); + const { isMobile } = useResponsiveStore(); + const { isPanelOpen, closePanel } = useLeftPanelStore(); const [isVisible, setIsVisible] = useState(false); const handleClick = (e: React.MouseEvent) => { e.preventDefault(); - const focusTarget = focusMainContentStart(); - if (focusTarget instanceof HTMLElement) { - focusTarget.scrollIntoView({ behavior: 'smooth', block: 'start' }); + const focusContent = () => { + const focusTarget = focusMainContentStart(); + + if (focusTarget instanceof HTMLElement) { + focusTarget.scrollIntoView({ behavior: 'smooth', block: 'start' }); + } + }; + + if (isMobile && isPanelOpen) { + closePanel(); + window.setTimeout(focusContent, LEFT_PANEL_CLOSE_TRANSITION_MS); + return; } + + focusContent(); }; + const leftBesideLogo = `calc(${spacingsTokens['sm']} + ${HEADER_ICON_WIDTH} + ${spacingsTokens['4xs']} + 70px + 12px)`; + const isMobilePanelClosed = isMobile && !isPanelOpen; + const isDocEditorPage = pathname === '/docs/[id]'; + + const left = isMobilePanelClosed + ? isDocEditorPage + ? `calc(${spacingsTokens['sm']} + ${COLLAPSE_BUTTON_WIDTH} + ${spacingsTokens['2xs']})` + : `calc(${spacingsTokens['sm']} + ${HEADER_ICON_WIDTH} + ${spacingsTokens['2xs']})` + : leftBesideLogo; + + const top = `calc(${HEADER_ROW_HEIGHT} / 2)`; + return (