️(frontend) fix share modal heading hierarchy

Render QuickSearchGroup names and link settings as h2 headings.
This commit is contained in:
Cyril 2026-03-16 11:09:25 +01:00
parent 4cd72ffa4f
commit 5020bc1c1a
No known key found for this signature in database
GPG key ID: D5E8474B0AB0064A
5 changed files with 46 additions and 38 deletions

View file

@ -10,6 +10,9 @@ and this project adheres to
- 🙈(docker) add **/.next to .dockerignore #2034
### Changed
- ♿️(frontend) ensure doc title is h1 for accessibility #2006
## [v4.8.1] - 2026-03-17
@ -35,7 +38,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) ensure doc title is h1 for accessibility #2006
### Fixed

View file

@ -55,7 +55,8 @@ test.describe('Header', () => {
'src',
'/assets/icon-docs-v2.svg',
);
await expect(header.locator('h1')).toBeHidden();
// With withTitle: false, the h1 is kept for accessibility but visually hidden via sr-only
await expect(header.locator('h1').getByText('Docs')).toHaveClass(/sr-only/);
});
test('checks a custom waffle', async ({ page }) => {

View file

@ -43,7 +43,7 @@ export const DocTitleText = () => {
return (
<Box className={CLASS_DOC_TITLE} $direction="row" $align="center">
<Text
as="h1"
as="h2"
$margin={{ all: 'none', left: 'none' }}
$size={isMobile ? 'h4' : 'h2'}
>
@ -168,39 +168,37 @@ const DocTitleInput = ({ doc }: DocTitleProps) => {
{!isTopRoot && <DocTitleEmojiPicker doc={doc} />}
<Tooltip content={t('Rename')} aria-hidden={true} placement="top">
<Box as="h1" $margin="none">
<Box
as="span"
role="textbox"
className="--docs--doc-title-input"
contentEditable
defaultValue={titleDisplay || undefined}
onKeyDownCapture={handleKeyDown}
suppressContentEditableWarning={true}
aria-label={`${t('Document title')}`}
aria-multiline={false}
onBlurCapture={(event) =>
handleTitleSubmit(event.target.textContent || '')
<Box
as="span"
role="textbox"
className="--docs--doc-title-input"
contentEditable
defaultValue={titleDisplay || undefined}
onKeyDownCapture={handleKeyDown}
suppressContentEditableWarning={true}
aria-label={`${t('Document title')}`}
aria-multiline={false}
onBlurCapture={(event) =>
handleTitleSubmit(event.target.textContent || '')
}
$padding={{ right: 'big' }}
$css={css`
&[contenteditable='true']:empty:not(:focus):before {
content: '${untitledDocument}';
color: var(
--c--contextuals--content--semantic--neutral--tertiary
);
pointer-events: none;
font-style: italic;
}
$padding={{ right: 'big' }}
$css={css`
&[contenteditable='true']:empty:not(:focus):before {
content: '${untitledDocument}';
color: var(
--c--contextuals--content--semantic--neutral--tertiary
);
pointer-events: none;
font-style: italic;
}
font-size: ${isDesktop
? css`var(--c--globals--font--sizes--h2)`
: css`var(--c--globals--font--sizes--sm)`};
font-weight: 700;
outline: none;
`}
>
{titleDisplay}
</Box>
font-size: ${isDesktop
? css`var(--c--globals--font--sizes--h2)`
: css`var(--c--globals--font--sizes--sm)`};
font-weight: 700;
outline: none;
`}
>
{titleDisplay}
</Box>
</Tooltip>
</Box>

View file

@ -76,7 +76,10 @@ export const Header = () => {
{...(({ withTitle: _, ...rest }) => rest)(icon)}
/>
)}
{icon?.withTitle && <Title headingLevel="h1" aria-hidden="true" />}
<Title
headingLevel="h1"
className={icon?.withTitle ? undefined : 'sr-only'}
/>
</Box>
</StyledLink>
{!isDesktop ? (

View file

@ -4,14 +4,18 @@ import { Text } from '@/components/';
type TitleSemanticsProps = {
headingLevel?: 'h1' | 'h2' | 'h3';
className?: string;
};
export const Title = ({ headingLevel = 'h2' }: TitleSemanticsProps) => {
export const Title = ({
headingLevel = 'h2',
className,
}: TitleSemanticsProps) => {
const { t } = useTranslation();
return (
<Text
className="--docs--title"
className={`--docs--title${className ? ` ${className}` : ''}`}
$direction="row"
$align="center"
$margin="none"