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:
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user