Enhance accessibility and improve form semantics across multiple pages
Build & Push Docker Image / build (push) Successful in 4m4s
Build & Push Docker Image / build (push) Successful in 4m4s
- Added `htmlFor` attributes to labels for better accessibility in Calendar, Dashboard, GuestJoin, Login, Register, RoomDetail, and Settings pages. - Included `aria-hidden` attributes for icons to improve screen reader experience. - Set `autoComplete` attributes for input fields to enhance user experience during form filling. - Implemented `role` and `aria` attributes for radio groups and buttons to improve accessibility compliance.
This commit is contained in:
@@ -1,15 +1,49 @@
|
||||
import { useEffect, useId, useRef } from 'react';
|
||||
import { X } from 'lucide-react';
|
||||
import { useLanguage } from '../contexts/LanguageContext';
|
||||
|
||||
export default function Modal({ title, children, onClose, maxWidth = 'max-w-lg' }) {
|
||||
const { t } = useLanguage();
|
||||
const titleId = useId();
|
||||
const dialogRef = useRef(null);
|
||||
const previouslyFocused = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
previouslyFocused.current = document.activeElement;
|
||||
const handleKey = (e) => {
|
||||
if (e.key === 'Escape') onClose?.();
|
||||
};
|
||||
document.addEventListener('keydown', handleKey);
|
||||
// Focus the dialog so screen readers announce it and keyboard focus lands inside
|
||||
dialogRef.current?.focus();
|
||||
return () => {
|
||||
document.removeEventListener('keydown', handleKey);
|
||||
previouslyFocused.current?.focus?.();
|
||||
};
|
||||
}, [onClose]);
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
|
||||
<div className="fixed inset-0 bg-black/60 backdrop-blur-xs" onClick={onClose} />
|
||||
<div className={`relative bg-th-card rounded-2xl border border-th-border shadow-2xl w-full ${maxWidth}`}>
|
||||
<div
|
||||
className="fixed inset-0 bg-black/60 backdrop-blur-xs"
|
||||
onClick={onClose}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<div
|
||||
ref={dialogRef}
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby={titleId}
|
||||
tabIndex={-1}
|
||||
className={`relative bg-th-card rounded-2xl border border-th-border shadow-2xl w-full ${maxWidth} focus:outline-hidden`}
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-th-border rounded-t-2xl">
|
||||
<h2 className="text-lg font-semibold text-th-text">{title}</h2>
|
||||
<h2 id={titleId} className="text-lg font-semibold text-th-text">{title}</h2>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
aria-label={t('common.close')}
|
||||
className="p-2 rounded-lg hover:bg-th-hover text-th-text-s transition-colors"
|
||||
>
|
||||
<X size={20} />
|
||||
|
||||
Reference in New Issue
Block a user