️(frontend) add global screen reader announcer

centralize live region rendering with a shared announce hook.
This commit is contained in:
Cyril
2026-01-28 11:44:39 +01:00
parent 9023e54352
commit f9dd2e1909
5 changed files with 39 additions and 1 deletions

View File

@@ -0,0 +1,15 @@
import { useCallback } from 'react'
import {
announceToScreenReader,
type Politeness,
} from '@/stores/screenReaderAnnouncer'
export const useScreenReaderAnnounce = () => {
return useCallback(
(message: string, politeness: Politeness = 'polite') => {
announceToScreenReader(message, politeness)
},
[]
)
}

View File

@@ -4,6 +4,7 @@ import { Header } from './Header'
import { layoutStore } from '@/stores/layout'
import { useSnapshot } from 'valtio'
import { Footer } from '@/layout/Footer'
import { ScreenReaderAnnouncer } from '@/primitives'
export type Layout = 'fullpage' | 'centered'
@@ -41,6 +42,7 @@ export const Layout = ({ children }: { children: ReactNode }) => {
flexDirection: 'column',
})}
>
<ScreenReaderAnnouncer />
{children}
</main>
</div>

View File

@@ -0,0 +1,20 @@
import { useSnapshot } from 'valtio'
import { screenReaderAnnouncerStore } from '@/stores/screenReaderAnnouncer'
export const ScreenReaderAnnouncer = () => {
const { announcement } = useSnapshot(screenReaderAnnouncerStore)
return (
<div
key={announcement.id}
role="status"
aria-live={announcement.politeness}
aria-atomic="true"
className="sr-only"
data-announce-id={announcement.id}
>
{announcement.message}
</div>
)
}

View File

@@ -24,6 +24,7 @@ export { Menu } from './Menu'
export { MenuList } from './MenuList'
export { P } from './P'
export { Popover } from './Popover'
export { ScreenReaderAnnouncer } from './ScreenReaderAnnouncer'
export { Text } from './Text'
export { ToggleButton } from './ToggleButton'
export { Ul } from './Ul'

View File

@@ -1,6 +1,6 @@
import { proxy } from 'valtio'
type Politeness = 'polite' | 'assertive'
export type Politeness = 'polite' | 'assertive'
type ScreenReaderAnnouncement = {
message: string