a11y: add ARIA live regions for status announcements and errors

This commit is contained in:
TypoGenie
2026-02-18 23:35:52 +02:00
parent d0f88625b5
commit e460f4df68
4 changed files with 40 additions and 17 deletions

View File

@@ -90,6 +90,7 @@ const App: React.FC = () => {
const [generatedHtml, setGeneratedHtml] = useState<string>('');
const [error, setError] = useState<string | null>(null);
const [showShortcuts, setShowShortcuts] = useState(false);
const [statusMessage, setStatusMessage] = useState('');
const { uiZoom, setUiZoom, isLoaded } = useSettings();
const { templates, categories, isLoading: templatesLoading, error: templatesError, refresh, openFolder } = useTemplates();
@@ -103,6 +104,17 @@ const App: React.FC = () => {
// The Tauri file drop is disabled to allow the webview to handle drag events
// This preserves the drag hover animations in the FileUpload component
// Announce state changes to screen readers
useEffect(() => {
const messages: Record<string, string> = {
[AppState.UPLOAD]: 'Upload screen',
[AppState.CONFIG]: 'Style configuration',
[AppState.GENERATING]: 'Generating document',
[AppState.PREVIEW]: 'Document preview',
};
setStatusMessage(messages[appState] || '');
}, [appState]);
// Global keydown listener for shortcuts help
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
@@ -168,7 +180,11 @@ const App: React.FC = () => {
};
if (!isLoaded) {
return null;
return (
<div className="h-screen w-screen flex items-center justify-center bg-zinc-950" role="status" aria-live="polite">
<span className="sr-only">Loading TypoGenie</span>
</div>
);
}
if (appState === AppState.PREVIEW) {
@@ -177,6 +193,7 @@ const App: React.FC = () => {
className="h-screen w-screen overflow-hidden"
style={{ fontSize: `${uiZoom}%` }}
>
<div role="status" aria-live="polite" className="sr-only">{statusMessage}</div>
<div className="h-full w-full flex flex-col">
<Preview
htmlContent={generatedHtml}
@@ -198,6 +215,7 @@ const App: React.FC = () => {
className="h-screen bg-zinc-950 text-zinc-100 flex flex-col font-sans selection:bg-indigo-500/30 overflow-hidden"
style={{ fontSize: `${uiZoom}%` }}
>
<div role="status" aria-live="polite" className="sr-only">{statusMessage}</div>
{/* Keyboard Shortcuts Modal */}
<KeyboardShortcutsHelp isOpen={showShortcuts} onClose={() => setShowShortcuts(false)} />
@@ -380,7 +398,6 @@ const App: React.FC = () => {
exit={{ opacity: 0, y: -10, scale: 0.95 }}
className="mt-4 p-4 bg-red-900/20 border border-red-800 rounded-xl text-center text-red-300"
role="alert"
aria-live="polite"
>
{error}
</motion.div>
@@ -392,6 +409,8 @@ const App: React.FC = () => {
{appState === AppState.GENERATING && (
<motion.div
key="generating"
role="status"
aria-busy="true"
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 1.1 }}