Skip to content

Commit 908c070

Browse files
accuserclaude
andcommitted
Keep accessible text honest and sleep between ticks
The visually-hidden text was written as HH:MM:SS but only refreshed when the minute changed, so assistive technology was given seconds that could be up to 59s stale. Format that text to the minute instead, matching the rate at which it is actually refreshed, rather than announcing a precision the value does not hold. Announcing every second would flood a polite live region, so the refresh rate stays as it was. Also drop aria-label from the <time> element: for an atomic live region the label wins over the contents in the accessible name computation, so it shadowed the very text it was meant to caption. tick() re-armed via requestAnimationFrame, waking ~60x/sec to do nothing in 59 of every 60 calls. Schedule the next tick on the second boundary instead; measured within 2ms of the boundary with no drift. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U3vf9sKKuCRHkwhBhGLpY1
1 parent a5a3e2d commit 908c070

2 files changed

Lines changed: 25 additions & 9 deletions

File tree

‎index.html‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
</head>
1111
<body>
1212
<div class="container">
13-
<time id="clock" aria-live="polite" aria-atomic="true" aria-label="Current time">
13+
<time id="clock" aria-live="polite" aria-atomic="true">
1414
<span class="visually-hidden" id="clock-text"></span>
1515
<template id="template">
1616
<div>

‎src/clock.js‎

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,22 @@ const start = (clock, template = clock.children[1]) => {
1717
* @param {Date} datetime - datetime to format
1818
* @return {string} the formatted datetime
1919
*/
20-
const formatTime = (datetime) => datetime.toLocaleTimeString('en-GB', { "hour12": false });
20+
const formatTime = (datetime) => datetime.toLocaleTimeString('en-GB', { hour12: false });
21+
22+
/**
23+
* Format the time to the minute, for the accessible text. Announcing every
24+
* second would flood assistive technology, so the text is deliberately no
25+
* more precise than the rate at which it is refreshed.
26+
*
27+
* @param {Date} datetime - datetime to format
28+
* @return {string} the formatted datetime
29+
*/
30+
const formatMinute = (datetime) =>
31+
datetime.toLocaleTimeString('en-GB', {
32+
hour12: false,
33+
hour: '2-digit',
34+
minute: '2-digit',
35+
});
2136

2237
const time = formatTime(new Date());
2338

@@ -42,11 +57,11 @@ const start = (clock, template = clock.children[1]) => {
4257
if (time !== lastTime) {
4358
clock.setAttribute('datetime', date);
4459

45-
// Only update accessible text when the minute changes (not every second)
46-
const currentMinute = time.substring(0, 5); // "HH:MM"
47-
if (currentMinute !== lastMinute) {
48-
clock.children.item(0).textContent = time;
49-
lastMinute = currentMinute;
60+
const minute = formatMinute(datetime);
61+
62+
if (minute !== lastMinute) {
63+
clock.children.item(0).textContent = minute;
64+
lastMinute = minute;
5065
}
5166

5267
for (let i = 0; i < time.length; i++) {
@@ -56,8 +71,9 @@ const start = (clock, template = clock.children[1]) => {
5671
lastTime = time;
5772
}
5873

59-
requestAnimationFrame(tick);
74+
// Sleep until the next second boundary rather than waking every frame
75+
setTimeout(tick, 1000 - (Date.now() % 1000));
6076
};
6177

62-
requestAnimationFrame(tick);
78+
tick();
6379
};

0 commit comments

Comments
 (0)