78eb58844e
Integrate a dark mode, an accessibility panel with options for dyslexia, high contrast, and text scaling, and enhance keyboard navigation. Update documentation to reflect these changes. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 923ae0e3-a363-4db8-b04a-e8baca2a1330 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: bbd001b6-1b5f-4425-9310-55a9081dabf8 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/8af7d2ec-2cc3-4ece-8af3-9f071488d072/923ae0e3-a363-4db8-b04a-e8baca2a1330/vOeFCU4 Replit-Helium-Checkpoint-Created: true
65 lines
2.4 KiB
TypeScript
65 lines
2.4 KiB
TypeScript
import React, { createContext, useContext, useState, useEffect, useCallback } from "react";
|
|
|
|
interface AccessibilityState {
|
|
darkMode: boolean;
|
|
dyslexiaFont: boolean;
|
|
highContrast: boolean;
|
|
largeText: boolean;
|
|
toggleDark: () => void;
|
|
toggleDyslexia: () => void;
|
|
toggleHighContrast: () => void;
|
|
toggleLargeText: () => void;
|
|
}
|
|
|
|
const AccessibilityContext = createContext<AccessibilityState | null>(null);
|
|
|
|
function load(key: string, fallback: boolean): boolean {
|
|
try {
|
|
const v = localStorage.getItem(key);
|
|
return v !== null ? v === "true" : fallback;
|
|
} catch {
|
|
return fallback;
|
|
}
|
|
}
|
|
|
|
function save(key: string, value: boolean) {
|
|
try { localStorage.setItem(key, String(value)); } catch { /* noop */ }
|
|
}
|
|
|
|
export function AccessibilityProvider({ children }: { children: React.ReactNode }) {
|
|
const [darkMode, setDarkMode] = useState(() => load("a11y-dark",
|
|
window.matchMedia?.("(prefers-color-scheme: dark)").matches ?? false
|
|
));
|
|
const [dyslexiaFont, setDyslexiaFont] = useState(() => load("a11y-dyslexia", false));
|
|
const [highContrast, setHighContrast] = useState(() => load("a11y-contrast", false));
|
|
const [largeText, setLargeText] = useState(() => load("a11y-large", false));
|
|
|
|
useEffect(() => {
|
|
const html = document.documentElement;
|
|
html.classList.toggle("dark", darkMode);
|
|
html.classList.toggle("dyslexia", dyslexiaFont);
|
|
html.classList.toggle("high-contrast", highContrast);
|
|
html.classList.toggle("large-text", largeText);
|
|
}, [darkMode, dyslexiaFont, highContrast, largeText]);
|
|
|
|
const toggleDark = useCallback(() => setDarkMode(v => { save("a11y-dark", !v); return !v; }), []);
|
|
const toggleDyslexia = useCallback(() => setDyslexiaFont(v => { save("a11y-dyslexia", !v); return !v; }), []);
|
|
const toggleHighContrast = useCallback(() => setHighContrast(v => { save("a11y-contrast", !v); return !v; }), []);
|
|
const toggleLargeText = useCallback(() => setLargeText(v => { save("a11y-large", !v); return !v; }), []);
|
|
|
|
return (
|
|
<AccessibilityContext.Provider value={{
|
|
darkMode, dyslexiaFont, highContrast, largeText,
|
|
toggleDark, toggleDyslexia, toggleHighContrast, toggleLargeText,
|
|
}}>
|
|
{children}
|
|
</AccessibilityContext.Provider>
|
|
);
|
|
}
|
|
|
|
export function useAccessibility() {
|
|
const ctx = useContext(AccessibilityContext);
|
|
if (!ctx) throw new Error("useAccessibility must be used inside AccessibilityProvider");
|
|
return ctx;
|
|
}
|