feat(a11y): add a Text size control and an OpenDyslexic font option (#4210)

* feat(a11y): add a Text size control and an OpenDyslexic font option

Text size: a Theme > Font & Layout control (Default / Larger) that scales the whole UI via CSS zoom, so the many hard-coded px sizes scale too (density only moves the root font-size). Stored globally so it persists across theme switches; applied early in the boot script to avoid a flash. OpenDyslexic: a dyslexia-friendly self-hosted font (SIL OFL 1.1), bundled as woff2 alongside Fira Code/Inter and wired into the Font select. Reuses the existing density/font pattern end to end; no new colours, spacing, or component styles.

* fix(a11y): keep modals on-screen at Larger text size

Inline vh heights on .modal-content overrode the ui-scale-125 max-height
compensation, so Cookbook (and the email/doc/skills/PDF modals) overflowed
the viewport at 125% — pushing the header and close button off-screen.
Let the compensation own those heights.

* fix(a11y): keep PDF export modal at its original 86vh on Default size
This commit is contained in:
Tom
2026-06-22 12:53:46 +01:00
committed by GitHub
parent d36879bd50
commit 91b4171b3f
12 changed files with 182 additions and 8 deletions
+37
View File
@@ -114,6 +114,10 @@ body {
@font-face { font-family: 'Fira Code'; font-weight: 400; font-style: normal; font-display: swap; src: url('/static/fonts/FiraCode-Regular.woff2') format('woff2'); }
@font-face { font-family: 'Fira Code'; font-weight: 600; font-style: normal; font-display: swap; src: url('/static/fonts/FiraCode-SemiBold.woff2') format('woff2'); }
/* Self-hosted OpenDyslexic — dyslexia-friendly accessibility font option (SIL OFL 1.1) */
@font-face { font-family: 'OpenDyslexic'; font-weight: 400; font-style: normal; font-display: swap; src: url('/static/fonts/OpenDyslexic-Regular.woff2') format('woff2'); }
@font-face { font-family: 'OpenDyslexic'; font-weight: 700; font-style: normal; font-display: swap; src: url('/static/fonts/OpenDyslexic-Bold.woff2') format('woff2'); }
/* Code block baseline */
pre, code, .hljs {
font-size: 0.95em;
@@ -158,6 +162,39 @@ html {
:root.density-spacious .list-item { padding: 8px 12px; }
:root.density-spacious .sidebar .section { padding: 0; }
/* UI text-size scale (accessibility)
Density only changes the root font-size, which can't move the many
hard-coded px sizes. `zoom` scales the whole UI uniformly (px text
included) while keeping layout intact, unlike `transform: scale`. */
:root.ui-scale-125 { zoom: 1.25; }
/* `zoom` makes the 100dvh shell render taller than the real viewport, which
pushes the bottom-pinned sidebar account/settings row below the fold (and
body's overflow:hidden then clips it). Shrink the shell by the same factor
so it fits the viewport exactly. */
:root.ui-scale-125 body { height: calc(100dvh / 1.25); }
/* Modals/panels under the 1.25x scale: zoom renders a centred, viewport-sized
panel ~1.25x taller, pushing its draggable header + close button off-screen
(a catch-22 you can't reach the control to turn the size back down). Divide
each max-height by the same factor to keep the original on-screen footprint.
Desktop only the mobile `!important` full-sheet rules win on small screens
and stay top-anchored, so their headers are already visible. */
:root.ui-scale-125 .modal-content { max-height: calc(85dvh / 1.25); }
:root.ui-scale-125 .cal-modal-content { max-height: calc(88dvh / 1.25); }
:root.ui-scale-125 .settings-modal-content { max-height: calc(85dvh / 1.25); }
:root.ui-scale-125 #theme-popup { max-height: min(calc(85dvh / 1.25), 480px); }
/* Cookbook is the one modal that set its height inline (94vh), which beat the
.modal-content compensation above and overflowed the viewport at 1.25x
(header + close button pushed off-screen). Own its height here so the same
zoom compensation applies. */
#cookbook-modal .modal-content { height: 94vh; max-height: 94vh; }
:root.ui-scale-125 #cookbook-modal .modal-content { height: calc(94dvh / 1.25); max-height: calc(94dvh / 1.25); }
/* PDF export modal also set its height inline (86vh) at v1.0; that inline cap
beat the .modal-content compensation above and shifted ~1vh at Default when
removed. Own its height here so Default is byte-for-byte 86vh and the same
1.25x compensation applies. */
.pdf-export-overlay .modal-content { max-height: 86vh; }
:root.ui-scale-125 .pdf-export-overlay .modal-content { max-height: calc(86dvh / 1.25); }
/* ── Background Patterns ── */
:root { --bg-effect-intensity: 1; }