♻️(frontend) improve and simplify accessibility font override logic

Fix compatibility issues with the DINUM frontend image, which
overrides the default `font-sans` value.

Simplify the implementation by having the JavaScript layer only
toggle well-scoped CSS classes responsible for accessibility font
overrides. This makes the behavior more predictable and restoring
default styles straightforward.

Also clarify the intent of the hook by making its accessibility
purpose explicit and moving its usage to the App component, where
it better fits the application lifecycle.
This commit is contained in:
lebaudantoine
2026-05-06 00:35:56 +02:00
committed by aleb_the_flash
parent 97b5e3e65c
commit f8937fc0a1
9 changed files with 54 additions and 64 deletions
+2
View File
@@ -14,12 +14,14 @@ import './i18n/init'
import { queryClient } from '@/api/queryClient'
import { AppInitialization } from '@/components/AppInitialization'
import { useIsSdkContext } from '@/features/sdk/hooks/useIsSdkContext'
import { useApplyA11yFonts } from '@/hooks/useApplyA11yFonts'
function App() {
const { i18n } = useTranslation()
useTitle(import.meta.env.VITE_APP_TITLE ?? '')
const isSDKContext = useIsSdkContext()
useApplyA11yFonts()
return (
<QueryClientProvider client={queryClient}>
@@ -0,0 +1,38 @@
import { useEffect } from 'react'
import { useSnapshot } from 'valtio'
import { accessibilityStore, UiFont } from '@/stores/accessibility'
const fontImports: Partial<Record<UiFont, () => Promise<unknown>>> = {
lexend: () => import('@fontsource-variable/lexend'),
'atkinson-hyperlegible': () =>
import('@fontsource-variable/atkinson-hyperlegible-next'),
opendyslexic: () => import('@fontsource/opendyslexic'),
}
const loadedFonts = new Set<UiFont>()
export function useApplyA11yFonts() {
const { uiFont } = useSnapshot(accessibilityStore)
useEffect(() => {
if (uiFont === 'default') {
return
}
const className = `font-${uiFont}`
const loader = fontImports[uiFont]
if (loader && !loadedFonts.has(uiFont)) {
loader().then(() => {
loadedFonts.add(uiFont)
document.documentElement.classList.add(className)
})
} else {
document.documentElement.classList.add(className)
}
return () => {
document.documentElement.classList.remove(className)
}
}, [uiFont])
}
-48
View File
@@ -1,48 +0,0 @@
import { useEffect } from 'react'
import { useSnapshot } from 'valtio'
import {
accessibilityStore,
UI_FONT_OVERRIDES,
UiFont,
} from '@/stores/accessibility'
const fontImports: Partial<Record<UiFont, () => Promise<unknown>>> = {
lexend: () => import('@fontsource-variable/lexend'),
'atkinson-hyperlegible': () =>
import('@fontsource-variable/atkinson-hyperlegible-next'),
opendyslexic: () => import('@fontsource/opendyslexic'),
}
const loadedFonts = new Set<UiFont>()
export function useApplyUiFont() {
const { uiFont } = useSnapshot(accessibilityStore)
useEffect(() => {
const loader = fontImports[uiFont]
if (loader && !loadedFonts.has(uiFont)) {
loader()
.then(() => loadedFonts.add(uiFont))
.catch((err) => {
console.error(
`[useApplyUiFont] Failed to load font "${uiFont}":`,
err
)
})
}
const override = UI_FONT_OVERRIDES[uiFont]
if (override) {
document.documentElement.style.setProperty(
'--app-font-family',
override
)
} else {
document.documentElement.style.removeProperty('--app-font-family')
}
return () => {
document.documentElement.style.removeProperty('--app-font-family')
}
}, [uiFont])
}
-3
View File
@@ -6,7 +6,6 @@ import { useSnapshot } from 'valtio'
import { Footer } from '@/layout/Footer'
import { ScreenReaderAnnouncer } from '@/primitives'
import { SkipLink, MAIN_CONTENT_ID } from './SkipLink'
import { useApplyUiFont } from '@/hooks/useApplyUiFont'
export type Layout = 'fullpage' | 'centered'
@@ -20,7 +19,6 @@ export const Layout = ({ children }: { children: ReactNode }) => {
const layoutSnap = useSnapshot(layoutStore)
const showHeader = layoutSnap.showHeader
const showFooter = layoutSnap.showFooter
useApplyUiFont()
return (
<>
@@ -32,7 +30,6 @@ export const Layout = ({ children }: { children: ReactNode }) => {
backgroundColor: 'white',
color: 'default.text',
flex: '1',
fontFamily: 'var(--app-font-family)',
})}
style={{
height: !showFooter ? '100%' : undefined,
+1 -1
View File
@@ -121,7 +121,7 @@
"label": "Display font",
"description": "Customize the font used in the interface to improve your reading comfort.",
"options": {
"default": "Default (system)",
"default": "Default",
"lexend": "Lexend (improved readability)",
"atkinson-hyperlegible": "Atkinson Hyperlegible (low vision)",
"opendyslexic": "OpenDyslexic (dyslexia)"
+1 -1
View File
@@ -121,7 +121,7 @@
"label": "Weergavelettertype",
"description": "Pas het lettertype van de interface aan om uw leescomfort te verbeteren.",
"options": {
"default": "Standaard (systeem)",
"default": "Standaard",
"lexend": "Lexend (verbeterde leesbaarheid)",
"atkinson-hyperlegible": "Atkinson Hyperlegible (slechtziend)",
"opendyslexic": "OpenDyslexic (dyslexie)"
-7
View File
@@ -15,13 +15,6 @@ export const UI_FONT_OPTIONS: UiFont[] = [
'opendyslexic',
]
export const UI_FONT_OVERRIDES: Partial<Record<UiFont, string>> = {
lexend: '"Lexend Variable", var(--app-font-family-base)',
'atkinson-hyperlegible':
'"Atkinson Hyperlegible Next Variable", var(--app-font-family-base)',
opendyslexic: 'OpenDyslexic, var(--app-font-family-base)',
}
export type CaptionTextSize = 'small' | 'medium' | 'large'
export const CAPTION_TEXT_SIZE_OPTIONS: CaptionTextSize[] = [
+11 -3
View File
@@ -6,9 +6,17 @@ body,
height: 100%;
}
:root {
--app-font-family-base: var(--fonts-sans);
--app-font-family: var(--app-font-family-base);
html.font-lexend {
--fonts-sans: 'Lexend Variable', ui-sans-serif, system-ui, sans-serif;
}
html.font-atkinson-hyperlegible {
--fonts-sans:
'Atkinson Hyperlegible Next Variable', ui-sans-serif, system-ui, sans-serif;
}
html.font-opendyslexic {
--fonts-sans: OpenDyslexic, ui-sans-serif, system-ui, sans-serif;
}
.sr-only {
+1 -1
View File
@@ -38,7 +38,7 @@
--lk-control-bar-height: 69px;
--lk-chat-header-height: 69px;
--lk-font-family: var(--app-font-family);
--lk-font-family: var(--fonts-sans);
--lk-font-size: 1rem;
--lk-bg6: #6b7280;
--lk-control-border-width: 1px;