(frontend) apply font preference to app layout

Hook, CSS variable and LiveKit integration for custom fonts.
This commit is contained in:
Cyril
2026-04-13 16:55:11 +02:00
committed by aleb_the_flash
parent 82d146cdf5
commit b917d82f7e
5 changed files with 62 additions and 19 deletions
+48
View File
@@ -0,0 +1,48 @@
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,6 +6,7 @@ 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'
@@ -19,6 +20,7 @@ export const Layout = ({ children }: { children: ReactNode }) => {
const layoutSnap = useSnapshot(layoutStore)
const showHeader = layoutSnap.showHeader
const showFooter = layoutSnap.showFooter
useApplyUiFont()
return (
<>
@@ -30,6 +32,7 @@ export const Layout = ({ children }: { children: ReactNode }) => {
backgroundColor: 'white',
color: 'default.text',
flex: '1',
fontFamily: 'var(--app-font-family)',
})}
style={{
height: !showFooter ? '100%' : undefined,
+5 -18
View File
@@ -15,24 +15,11 @@ export const UI_FONT_OPTIONS: UiFont[] = [
'opendyslexic',
]
const SYSTEM_SANS_STACK = [
'ui-sans-serif',
'system-ui',
'-apple-system',
'BlinkMacSystemFont',
'"Segoe UI"',
'Roboto',
'"Helvetica Neue"',
'Arial',
'"Noto Sans"',
'sans-serif',
].join(', ')
export const UI_FONT_STACKS: Record<UiFont, string> = {
default: SYSTEM_SANS_STACK,
lexend: `"Lexend Variable", ${SYSTEM_SANS_STACK}`,
'atkinson-hyperlegible': `"Atkinson Hyperlegible Next", ${SYSTEM_SANS_STACK}`,
opendyslexic: `OpenDyslexic, ${SYSTEM_SANS_STACK}`,
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'
+5
View File
@@ -6,6 +6,11 @@ body,
height: 100%;
}
:root {
--app-font-family-base: var(--fonts-sans);
--app-font-family: var(--app-font-family-base);
}
.sr-only {
position: absolute;
width: 1px;
+1 -1
View File
@@ -38,7 +38,7 @@
--lk-control-bar-height: 69px;
--lk-chat-header-height: 69px;
--lk-font-family: var(--fonts-sans);
--lk-font-family: var(--app-font-family);
--lk-font-size: 1rem;
--lk-bg6: #6b7280;
--lk-control-border-width: 1px;