diff --git a/package-lock.json b/package-lock.json index a11e4d4..bc25ea5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,14 @@ { "name": "vega", - "version": "0.12.16", + "version": "0.12.17", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vega", - "version": "0.12.16", + "version": "0.12.17", "dependencies": { + "@fontsource/atkinson-hyperlegible": "5.2.8", "@nostr-dev-kit/ndk": "^3.0.3", "@tailwindcss/vite": "^4.2.1", "@tanstack/react-virtual": "3.13.24", @@ -1015,6 +1016,15 @@ } } }, + "node_modules/@fontsource/atkinson-hyperlegible": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@fontsource/atkinson-hyperlegible/-/atkinson-hyperlegible-5.2.8.tgz", + "integrity": "sha512-HciLcJ5DIK/OVOdo71EbEN4NnvDFlp6/SpAxtcbWf2aAdcsOuPqITxj5KNEXb48qSPSdnnZdGGnSJChPKi3/bA==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", diff --git a/package.json b/package.json index 7902033..a74cf02 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "test:run": "vitest run" }, "dependencies": { + "@fontsource/atkinson-hyperlegible": "5.2.8", "@nostr-dev-kit/ndk": "^3.0.3", "@tailwindcss/vite": "^4.2.1", "@tanstack/react-virtual": "3.13.24", diff --git a/src/App.tsx b/src/App.tsx index ef123fd..e56a1d0 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -96,6 +96,7 @@ function App() { const showDebugPanel = useUIStore((s) => s.showDebugPanel); const toggleDebugPanel = useUIStore((s) => s.toggleDebugPanel); const fontSize = useUIStore((s) => s.fontSize); + const easyReadFont = useUIStore((s) => s.easyReadFont); const themeId = useUIStore((s) => s.themeId); const [onboardingDone, setOnboardingDone] = useState( () => !!localStorage.getItem("wrystr_pubkey") @@ -117,6 +118,11 @@ function App() { if (theme) applyTheme(theme); }, [themeId]); + // Apply easy-read font class on + useEffect(() => { + document.documentElement.classList.toggle("font-readable", easyReadFont); + }, [easyReadFont]); + // Intercept external link clicks and open in system browser via Tauri opener useEffect(() => { const handler = (e: MouseEvent) => { diff --git a/src/components/shared/SettingsView.tsx b/src/components/shared/SettingsView.tsx index d6c4547..b130111 100644 --- a/src/components/shared/SettingsView.tsx +++ b/src/components/shared/SettingsView.tsx @@ -434,6 +434,36 @@ function FontSizeSection() { ); } +function EasyReadFontSection() { + const { easyReadFont, setEasyReadFont } = useUIStore(); + + return ( +
+

+ Easy-Read Font +

+
+ +
+

+ Switches the UI to Atkinson Hyperlegible — a font designed by the + Braille Institute for legibility. Helps dyslexic readers and anyone + reading long sessions; slightly wider letter-spacing and line-height + applied per evidence-based guidance. +

+
+ ); +} + function formatBytes(bytes: number): string { if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; @@ -515,6 +545,7 @@ export function SettingsView() {
+ diff --git a/src/index.css b/src/index.css index 3a905c5..9b7a94a 100644 --- a/src/index.css +++ b/src/index.css @@ -46,6 +46,18 @@ body { overflow: hidden; } +/* Easy-read font — Atkinson Hyperlegible — toggleable in Settings. + Designed by the Braille Institute for legibility; helps dyslexic readers + and anyone reading long sessions. Letter-spacing and line-height nudged + per evidence-based guidance. */ +html.font-readable { + --font-ui: "Atkinson Hyperlegible", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; +} +html.font-readable body { + letter-spacing: 0.012em; + line-height: 1.65; +} + /* Allow text selection in article editor */ .article-editor textarea, .article-editor input { diff --git a/src/main.tsx b/src/main.tsx index e3322fb..92beaf8 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -4,6 +4,8 @@ import { debug } from "./lib/debug"; import { createRoot } from "react-dom/client"; import App from "./App"; import "./index.css"; +import "@fontsource/atkinson-hyperlegible/400.css"; +import "@fontsource/atkinson-hyperlegible/700.css"; import { useUserStore } from "./stores/user"; // Error boundary to catch React error #31 and show details instead of blank screen diff --git a/src/stores/ui.ts b/src/stores/ui.ts index 23408bb..abc7b14 100644 --- a/src/stores/ui.ts +++ b/src/stores/ui.ts @@ -31,6 +31,7 @@ interface UIState { feedLanguageFilter: string | null; followsTab: "followers" | "following"; fontSize: number; + easyReadFont: boolean; themeId: string; setView: (view: View) => void; setFollowsTab: (tab: "followers" | "following") => void; @@ -44,6 +45,7 @@ interface UIState { goBack: () => void; setFeedLanguageFilter: (filter: string | null) => void; setFontSize: (size: number) => void; + setEasyReadFont: (on: boolean) => void; setTheme: (id: string) => void; toggleSidebar: () => void; toggleHelp: () => void; @@ -52,6 +54,7 @@ interface UIState { const SIDEBAR_KEY = "wrystr_sidebar_collapsed"; const FONT_SIZE_KEY = "wrystr_font_size"; +const EASY_READ_FONT_KEY = "wrystr_easy_read_font"; const THEME_KEY = "wrystr_theme"; const SCRIPT_FILTER_KEY = "wrystr_script_filter"; @@ -73,6 +76,7 @@ export const useUIStore = create((set, _get) => ({ feedLanguageFilter: localStorage.getItem(SCRIPT_FILTER_KEY) || null, followsTab: "followers", fontSize: parseInt(localStorage.getItem(FONT_SIZE_KEY) || "14", 10), + easyReadFont: localStorage.getItem(EASY_READ_FONT_KEY) === "true", themeId: localStorage.getItem(THEME_KEY) || "midnight", setView: (currentView) => set({ currentView }), setFeedTab: (feedTab) => set({ feedTab }), @@ -115,6 +119,10 @@ export const useUIStore = create((set, _get) => ({ localStorage.setItem(FONT_SIZE_KEY, String(fontSize)); set({ fontSize }); }, + setEasyReadFont: (easyReadFont) => { + localStorage.setItem(EASY_READ_FONT_KEY, String(easyReadFont)); + set({ easyReadFont }); + }, setTheme: (themeId) => { localStorage.setItem(THEME_KEY, themeId); set({ themeId });