Prashikshak / Web /src /components /AccessibilityMenu.tsx
Abhisingh-18's picture
Initial commit: Prashikshak - disaster management training platform
9a92a42
Raw
History Blame Contribute Delete
9.27 kB
import { useState, useEffect } from 'react';
import { Type, Sun, MonitorOff, RotateCcw, Accessibility, Eye, Volume2 } from 'lucide-react';
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { useTranslation } from 'react-i18next';
const AccessibilityMenu = () => {
const { i18n } = useTranslation();
const [fontSize, setFontSize] = useState(100);
const [highContrast, setHighContrast] = useState(false);
const [grayscale, setGrayscale] = useState(false);
const [readableFont, setReadableFont] = useState(false);
const [screenReader, setScreenReader] = useState(false);
// Apply changes to the document
useEffect(() => {
const root = document.documentElement;
// Font Size
root.style.fontSize = `${fontSize}%`;
// High Contrast
if (highContrast) {
root.classList.add('high-contrast');
document.body.style.backgroundColor = '#000';
document.body.style.color = '#fff';
// Force specific elements to contrast
const cards = document.querySelectorAll('.bg-white');
cards.forEach((el: any) => {
el.style.backgroundColor = '#1a1a1a';
el.style.color = '#fff';
el.style.borderColor = '#333';
});
} else {
root.classList.remove('high-contrast');
document.body.style.backgroundColor = '';
document.body.style.color = '';
const cards = document.querySelectorAll('.bg-white');
cards.forEach((el: any) => {
el.style.backgroundColor = '';
el.style.color = '';
el.style.borderColor = '';
});
}
// Grayscale
if (grayscale) {
root.style.filter = 'grayscale(100%)';
} else {
root.style.filter = '';
}
// Readable Font
if (readableFont) {
document.body.classList.add('readable-font');
document.body.style.fontFamily = 'Arial, Helvetica, sans-serif';
document.body.style.lineHeight = '1.8';
document.body.style.letterSpacing = '0.05em';
} else {
document.body.classList.remove('readable-font');
document.body.style.fontFamily = '';
document.body.style.lineHeight = '';
document.body.style.letterSpacing = '';
}
}, [fontSize, highContrast, grayscale, readableFont]);
// Simple Screen Reader Simulation (reads hovered text)
useEffect(() => {
if (!screenReader) {
window.speechSynthesis.cancel();
return;
}
const handleMouseOver = (e: MouseEvent) => {
const target = e.target as HTMLElement;
if (target && (target.innerText || target.getAttribute('aria-label'))) {
window.speechSynthesis.cancel();
const text = target.getAttribute('aria-label') || target.innerText;
if (text && text.trim().length > 0) {
// Filter out long text blocks to avoid annoyance
if (text.length < 200) {
const utterance = new SpeechSynthesisUtterance(text);
// Try to match language
utterance.lang = i18n.language === 'hi' ? 'hi-IN' : 'en-US';
window.speechSynthesis.speak(utterance);
}
}
}
};
document.addEventListener('mouseover', handleMouseOver);
return () => {
document.removeEventListener('mouseover', handleMouseOver);
window.speechSynthesis.cancel();
};
}, [screenReader, i18n.language]);
const resetSettings = () => {
setFontSize(100);
setHighContrast(false);
setGrayscale(false);
setReadableFont(false);
setScreenReader(false);
};
return (
<div className="fixed bottom-6 right-6 z-[100]">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="icon"
className="h-14 w-14 rounded-full shadow-2xl bg-blue-600 hover:bg-blue-700 text-white border-4 border-white"
aria-label="Accessibility Options"
>
<Accessibility className="h-8 w-8" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-72 p-4 bg-white/95 backdrop-blur-sm shadow-xl border-blue-100">
<DropdownMenuLabel className="flex items-center justify-between text-lg">
<span>Accessibility Tools</span>
<Button variant="ghost" size="sm" onClick={resetSettings} className="h-8 px-2 text-xs text-red-500 hover:text-red-700 hover:bg-red-50">
<RotateCcw className="h-3 w-3 mr-1" /> Reset
</Button>
</DropdownMenuLabel>
<DropdownMenuSeparator />
{/* Font Size Control */}
<div className="py-3">
<div className="text-sm font-medium mb-3 flex items-center text-gray-700">
<Type className="h-4 w-4 mr-2" /> Text Size ({fontSize}%)
</div>
<div className="flex gap-2">
<Button
variant="outline"
size="sm"
className="flex-1 border-gray-200 hover:bg-blue-50 hover:border-blue-200 hover:text-blue-600"
onClick={() => setFontSize(Math.max(80, fontSize - 10))}
disabled={fontSize <= 80}
>
A-
</Button>
<Button
variant="outline"
size="sm"
className="flex-1 border-gray-200 hover:bg-blue-50 hover:border-blue-200 hover:text-blue-600"
onClick={() => setFontSize(Math.min(150, fontSize + 10))}
disabled={fontSize >= 150}
>
A+
</Button>
</div>
</div>
<DropdownMenuSeparator className="my-1"/>
{/* Display Options */}
<div className="space-y-2 py-2">
<Button
variant={highContrast ? "default" : "ghost"}
className={`w-full justify-start ${highContrast ? 'bg-black text-white hover:bg-gray-800' : 'hover:bg-gray-100 text-gray-700'}`}
onClick={() => setHighContrast(!highContrast)}
>
<Sun className="h-4 w-4 mr-3" />
{highContrast ? 'Disable High Contrast' : 'High Contrast Mode'}
</Button>
<Button
variant={grayscale ? "default" : "ghost"}
className={`w-full justify-start ${grayscale ? 'bg-gray-600 hover:bg-gray-700' : 'hover:bg-gray-100 text-gray-700'}`}
onClick={() => setGrayscale(!grayscale)}
>
<MonitorOff className="h-4 w-4 mr-3" />
{grayscale ? 'Disable Grayscale' : 'Grayscale Mode'}
</Button>
<Button
variant={readableFont ? "default" : "ghost"}
className={`w-full justify-start ${readableFont ? 'bg-blue-600 hover:bg-blue-700' : 'hover:bg-gray-100 text-gray-700'}`}
onClick={() => setReadableFont(!readableFont)}
>
<Eye className="h-4 w-4 mr-3" />
{readableFont ? 'Disable Readable Font' : 'Dyslexia Friendly Font'}
</Button>
<Button
variant={screenReader ? "default" : "ghost"}
className={`w-full justify-start ${screenReader ? 'bg-green-600 hover:bg-green-700' : 'hover:bg-gray-100 text-gray-700'}`}
onClick={() => setScreenReader(!screenReader)}
>
<Volume2 className="h-4 w-4 mr-3" />
{screenReader ? 'Turn Off Screen Reader' : 'Screen Reader (Hover)'}
</Button>
</div>
</DropdownMenuContent>
</DropdownMenu>
</div>
);
};
export default AccessibilityMenu;