a11y: add ARIA live regions for status announcements and errors
This commit is contained in:
23
src/App.tsx
23
src/App.tsx
@@ -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 }}
|
||||
|
||||
Reference in New Issue
Block a user