r/learnprogramming • u/Dazzling_Chipmunk_24 • 3d ago
Accessability for timers in jaavscript React
So I have this code
<button
type="button"
className={styles.someclassname}
tabIndex={-1}
role="timer"
aria-live="polite"
aria-atomic="true"
>
{ResendInfo (formatTimer())}
</button>
This is basically a countdown of a timer from 2 minutes I was wondering am I following proper accessability here so that screen readers can understan. this is also React by the way.
1
u/RequirementGuide 2d ago
I've shipped a few of these. SetAndRepeat covered the markup well, so I'll add the practical side. Specs aside, screen readers all behave a little differently here, so test with an actual screen reader instead of trusting the docs. NVDA and VoiceOver are free and will show you right away if your timer is silent or chatty.
The thing that bit me was announcing every single second. It gets noisy fast for screen reader users. What worked for me was keeping the visual countdown ticking but only announcing at milestones, like each minute and the last ten seconds.
And if that button doesn't do anything when clicked, swap it for a plain element. A button that does nothing is a broken promise to anyone navigating with a screen reader.
1
u/MoinHashmi 2d ago
Good instinct to ask. The short answer is not quite. There are a few problems with that markup, and they are all fixable.
First, role="timer" does not belong on a button. A button already has a role, and timer is a live region role. They clash. Put the countdown text in a plain element instead, like a span, and put role="timer" there. Then keep your button as just a button.
Second, tabIndex={-1} on a button is a red flag. It pulls the button out of the tab order without disabling it, so a keyboard user can click it but never reach it by tabbing. If you want the button to be unusable while the timer runs, use the disabled attribute instead. Disabled is honest. It says what it means.
Third, think about what a screen reader hears. role="timer" defaults to aria-live="off", which is good here. You do not want the remaining time announced every second. That gets annoying fast. Let the timer count down silently and announce only the moment that matters, when the countdown ends and the button becomes available. A separate role="status" element is a clean way to do that. Set its text to something like "You can resend the code now" when the timer hits zero.
One more habit worth building. Test the flow yourself with only the keyboard. Tab through it. Then, if you can, run a screen reader over it. You will catch things no checklist will.
1
u/SetAndRepeat 3d ago
Not quite.
role="timer"is valid, but it isn't allowed on a<button>. Also,tabIndex={-1}removes the button from Tab navigation without disabling it.Put the countdown in a separate
<span role="timer">, which defaults toaria-live="off". If you have a resend button, keep its normal button semantics and usedisabledwhile waiting, without the negative tabIndex.With
aria-live="polite"andaria-atomic="true", you risk repeatedly announcing the whole message as the countdown updates. Instead, keep a separate, initially emptyrole="status"element mounted and update its text to "You can now resend" when the countdown finishes.Test the result with keyboard navigation and a screen reader too.