fix(BOOK-468): Added inert attribute to SidePeekSEO element to ignore tab navigation and screen readers

Approved-by: Linus Flood
This commit is contained in:
Erik Tiekstra
2025-11-24 14:12:10 +00:00
parent acb50eb75e
commit 091c1c3780
6 changed files with 23 additions and 19 deletions

View File

@@ -2,6 +2,8 @@ import ButtonLink from "@scandic-hotels/design-system/ButtonLink"
import SidePeek from "@scandic-hotels/design-system/SidePeek"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { env } from "@/env/server"
import { getIntl } from "@/i18n"
import { appendSlugToPathname } from "@/utils/appendSlugToPathname"
@@ -21,6 +23,7 @@ export default async function MeetingsAndConferencesSidePeek({
heading,
}: MeetingsAndConferencesSidePeekProps) {
const intl = await getIntl()
const shouldInert = env.SEO_INERT
const { seatingText, roomText } = await getConferenceRoomTexts(meetingRooms)
const visibleImages = meetingFacilities?.heroImages.slice(0, 2)
const meetingPageHref = await appendSlugToPathname(meetingPageUrl)
@@ -33,6 +36,7 @@ export default async function MeetingsAndConferencesSidePeek({
id: "common.close",
defaultMessage: "Close",
})}
shouldInert={shouldInert}
>
<div className={styles.wrapper}>
<Typography variant="Title/Subtitle/lg">

View File

@@ -107,6 +107,11 @@ export const env = createEnv({
// transform to boolean
.transform((s) => s === "true")
.default("false"),
SEO_INERT: z
.string()
.refine((s) => s === "1" || s === "0")
.transform((s) => s === "1")
.default("0"),
},
emptyStringAsUndefined: true,
runtimeEnv: {
@@ -162,5 +167,6 @@ export const env = createEnv({
HOTEL_BRANDING: process.env.HOTEL_BRANDING,
CHATBOT_LIVE_LANGS: process.env.CHATBOT_LIVE_LANGS,
NEW_STAYS_ON_MY_PAGES: process.env.NEW_STAYS_ON_MY_PAGES,
SEO_INERT: process.env.SEO_INERT,
},
})

View File

@@ -1,16 +0,0 @@
import type { SidePeekSelfControlledProps } from './sidePeek'
// Sidepeeks generally have important content that should be indexed by search engines.
// The content is hidden behind a modal, but it is still important for SEO.
// This component is used to provide SEO information for the sidepeek content.
export default function SidePeekSEO({
title,
children,
}: React.PropsWithChildren<Pick<SidePeekSelfControlledProps, 'title'>>) {
return (
<div className="sr-only">
<h2>{title}</h2>
{children}
</div>
)
}

View File

@@ -11,7 +11,7 @@ import { IconButton } from '../../IconButton'
import { MaterialIcon } from '../../Icons/MaterialIcon'
import { Typography } from '../../Typography'
import SidePeekSEO from './SidePeekSEO'
import SidePeekSEO from '../SidePeekSEO'
import styles from './sidePeekSelfControlled.module.css'

View File

@@ -1,5 +1,6 @@
interface SidePeekSEOProps {
title: string
shouldInert?: boolean
}
// Sidepeeks generally have important content that should be indexed by search engines.
@@ -7,10 +8,15 @@ interface SidePeekSEOProps {
// This component is used to provide SEO information for the sidepeek content.
export default function SidePeekSEO({
title,
shouldInert = false,
children,
}: React.PropsWithChildren<SidePeekSEOProps>) {
return (
<div className="sr-only">
// Both inert and sr-only to ensure that the content is not focusable
// or visible to screen readers but still available for SEO.
// The other possible options, such as aria-hidden and the hidden attribute,
// are less suitable for SEO purposes.
<div className="sr-only" inert={shouldInert}>
<h2>{title}</h2>
{children}
</div>

View File

@@ -20,6 +20,7 @@ interface SidePeekProps {
openInRoot?: boolean
handleClose?: (isOpen: boolean) => void
closeLabel: string
shouldInert?: boolean
}
export default function SidePeek({
@@ -30,6 +31,7 @@ export default function SidePeek({
isOpen,
openInRoot = false,
closeLabel,
shouldInert,
}: React.PropsWithChildren<SidePeekProps>) {
const rootDiv = useRef<HTMLDivElement>(null)
const headerRef = useRef<HTMLElement>(null)
@@ -116,7 +118,9 @@ export default function SidePeek({
</ModalOverlay>
</div>
<SidePeekSEO title={title}>{children}</SidePeekSEO>
<SidePeekSEO title={title} shouldInert={shouldInert}>
{children}
</SidePeekSEO>
</>
)
}