Task 15: Accessibility audit complete
- Sidebar: Replace <aside role="navigation"> with <nav> to avoid conflicting roles
- Sidebar search: Add combobox role, aria-expanded, aria-controls, aria-autocomplete
- Search results: Add listbox/option roles, group labels for screen reader navigation
- PMRInterface: Remove redundant role="main", fix aria-label to use CV-friendly labels
- Mobile search: Add aria-label and type="search" for proper semantics
- Breadcrumb: Add aria-current="page" to current item, aria-hidden on separators
- Clinical alert: Add aria-label="Acknowledge clinical alert" on button per spec
- Patient banner: Change focus:ring to focus-visible:ring on action buttons
- Patient banner: Add role="img" to StatusDot for aria-label accessibility
- Login screen: Change role="status" to role="dialog" with aria-modal
- Login screen: Add loginButtonRef with auto-focus when typing completes
- Login screen: Add focus-visible ring style to Log In button
- Medications tabs: Add id="tab-{id}" to tab buttons, fix aria-labelledby on panels
- Consultations: Wrap entries in <article> per semantic HTML spec
- Problems: Change TrafficLight dot from role="img" to aria-hidden (text label handles it)
- App: Add sr-only live region announcing "Patient Record for Charlwood, Andrew" on PMR entry
- Skip button: Add focus-visible ring for keyboard users
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -30,6 +30,7 @@ export function LoginScreen({ onComplete }: LoginScreenProps) {
|
||||
const passwordIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null)
|
||||
const cursorIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null)
|
||||
const timeoutRefs = useRef<ReturnType<typeof setTimeout>[]>([])
|
||||
const loginButtonRef = useRef<HTMLButtonElement>(null)
|
||||
|
||||
const addTimeout = useCallback((fn: () => void, delay: number) => {
|
||||
const id = setTimeout(fn, delay)
|
||||
@@ -92,6 +93,13 @@ export function LoginScreen({ onComplete }: LoginScreenProps) {
|
||||
}, 80)
|
||||
}, [prefersReducedMotion, addTimeout])
|
||||
|
||||
// Focus the login button when typing completes for keyboard accessibility
|
||||
useEffect(() => {
|
||||
if (typingComplete && loginButtonRef.current) {
|
||||
loginButtonRef.current.focus()
|
||||
}
|
||||
}, [typingComplete])
|
||||
|
||||
useEffect(() => {
|
||||
// Cursor blink: 530ms interval
|
||||
cursorIntervalRef.current = setInterval(() => {
|
||||
@@ -125,8 +133,9 @@ export function LoginScreen({ onComplete }: LoginScreenProps) {
|
||||
<div
|
||||
className="fixed inset-0 flex items-center justify-center z-50"
|
||||
style={{ backgroundColor: '#1E293B' }}
|
||||
role="status"
|
||||
role="dialog"
|
||||
aria-label="Clinical system login"
|
||||
aria-modal="true"
|
||||
>
|
||||
<motion.div
|
||||
className="bg-white"
|
||||
@@ -273,10 +282,12 @@ export function LoginScreen({ onComplete }: LoginScreenProps) {
|
||||
|
||||
{/* Log In Button — user clicks to proceed */}
|
||||
<button
|
||||
ref={loginButtonRef}
|
||||
onClick={handleLogin}
|
||||
disabled={!typingComplete}
|
||||
onMouseEnter={() => setButtonHovered(true)}
|
||||
onMouseLeave={() => setButtonHovered(false)}
|
||||
className="focus-visible:ring-2 focus-visible:ring-[#005EB8]/40 focus-visible:ring-offset-2 focus:outline-none"
|
||||
style={{
|
||||
width: '100%',
|
||||
padding: '10px 16px',
|
||||
|
||||
Reference in New Issue
Block a user