Add dark mode and accessibility features for improved user experience

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
This commit is contained in:
pironantoine
2026-04-04 11:45:46 +00:00
parent 176b49d796
commit 78eb58844e
8 changed files with 365 additions and 51 deletions
@@ -0,0 +1,64 @@
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;
}