mirror of
https://github.com/hoornet/vega.git
synced 2026-06-08 14:11:55 -07:00
feat: easy-read font option (Atkinson Hyperlegible)
New Settings → Appearance toggle, "Easy-Read Font". When on, swaps the UI sans-serif to Atkinson Hyperlegible — a typeface designed by the Braille Institute for legibility (distinct b/d/p/q, clear 1/I/l). Helps dyslexic readers and anyone in a long reading session. - Bundled via @fontsource/atkinson-hyperlegible (400 + 700), pinned exact — no runtime font fetch, works offline. - Toggle adds an html.font-readable class; CSS overrides --font-ui and nudges letter-spacing (+0.012em) and line-height (1.6 -> 1.65) per evidence-based dyslexia guidance. - Persisted to localStorage (wrystr_easy_read_font), applied on <html> at startup. Independent of theme — pairs with any, Reader included. - Code blocks keep font-mono; articles keep their reading serif. Follow-up to the Reader theme: colour was ~60-70% of the dyslexia- friendly benefit, the font is most of the rest.
This commit is contained in:
Generated
+12
-2
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 <html>
|
||||
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) => {
|
||||
|
||||
@@ -434,6 +434,36 @@ function FontSizeSection() {
|
||||
);
|
||||
}
|
||||
|
||||
function EasyReadFontSection() {
|
||||
const { easyReadFont, setEasyReadFont } = useUIStore();
|
||||
|
||||
return (
|
||||
<section>
|
||||
<h2 className="text-text text-[11px] font-medium uppercase tracking-widest mb-2 text-text-dim">
|
||||
Easy-Read Font
|
||||
</h2>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<button
|
||||
onClick={() => setEasyReadFont(!easyReadFont)}
|
||||
className={`px-3 py-1.5 text-[11px] border transition-colors ${
|
||||
easyReadFont
|
||||
? "border-accent text-accent"
|
||||
: "border-border text-text-muted hover:text-text hover:border-accent/40"
|
||||
}`}
|
||||
>
|
||||
{easyReadFont ? "On" : "Off"}
|
||||
</button>
|
||||
</div>
|
||||
<p className="text-text-dim text-[10px] mt-2 px-1">
|
||||
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.
|
||||
</p>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
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() {
|
||||
<div className="flex-1 overflow-y-auto p-4 space-y-8">
|
||||
<ThemeSection />
|
||||
<FontSizeSection />
|
||||
<EasyReadFontSection />
|
||||
<WalletSection />
|
||||
<NotificationSection />
|
||||
<ExperimentalSection />
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<UIState>((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<UIState>((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 });
|
||||
|
||||
Reference in New Issue
Block a user