Loading Developer Playground

Loading ...

Skip to main content
ARIA ROLELive Region Roles

timer

A type of live region containing a numerical counter which indicates an amount of elapsed time from a start point, or remaining time until an end point.

Implicit aria-live
off
Implicit aria-atomic
false
Required Attributes
None

Overview

The timer role indicates a numerical counter for elapsed or remaining time. By default, timer updates are not announced (aria-live="off") to prevent overwhelming users with constant updates.

You can selectively enable announcements at important milestones by temporarily changing aria-live to "polite" or "assertive" when needed.

Silent by Default

Imagine if every second of a countdown was announced - it would be unbearable! That's why timers have aria-live="off" by default. Only announce at meaningful moments (1 minute left, 10 seconds left, etc.).

Live Demo: Countdown Timer

1:00

Note: Timer updates are NOT announced by default. In production, you would programmatically enable announcements at key moments (e.g., "1 minute remaining", "10 seconds remaining").

Code Examples

Basic Timer

<!-- Basic Timer -->
<div role="timer" aria-label="Session Timer">
  <span id="minutes">05</span>:<span id="seconds">00</span>
</div>

<!-- Note: By default, timer updates are NOT announced (aria-live="off") -->
<!-- Only announce at significant milestones -->

Countdown with Milestones

<!-- Countdown Timer with Announcements -->
<div id="countdown" 
     role="timer" 
     aria-live="off"
     aria-label="Quiz Time Remaining">
  <strong id="time-display">10:00</strong>
</div>

<script>
  let timeLeft = 600; // 10 minutes in seconds
  
  const timer = setInterval(() => {
    timeLeft--;
    
    const minutes = Math.floor(timeLeft / 60);
    const seconds = timeLeft % 60;
    document.getElementById('time-display').textContent = 
      `${minutes}:${seconds.toString().padStart(2, '0')}`;
    
    // Announce at important milestones
    const timerEl = document.getElementById('countdown');
    if (timeLeft === 60) {
      timerEl.setAttribute('aria-live', 'polite');
      timerEl.setAttribute('aria-atomic', 'true');
      // Will announce: "Quiz Time Remaining: 1:00"
      setTimeout(() => timerEl.setAttribute('aria-live', 'off'), 1000);
    }
    
    if (timeLeft === 0) {
      clearInterval(timer);
      timerEl.setAttribute('aria-live', 'assertive');
      timerEl.textContent = 'Time is up!';
    }
  }, 1000);
</script>

Stopwatch

<!-- Stopwatch / Elapsed Time -->
<div role="timer" aria-label="Elapsed Time">
  <span id="elapsed">00:00:00</span>
</div>

<button id="start">Start</button>
<button id="stop">Stop</button>
<button id="reset">Reset</button>

<script>
  let startTime;
  let interval;
  
  function updateDisplay() {
    const elapsed = Date.now() - startTime;
    const hours = Math.floor(elapsed / 3600000);
    const minutes = Math.floor((elapsed % 3600000) / 60000);
    const seconds = Math.floor((elapsed % 60000) / 1000);
    
    document.getElementById('elapsed').textContent = 
      `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
  }
  
  document.getElementById('start').addEventListener('click', () => {
    startTime = Date.now();
    interval = setInterval(updateDisplay, 100);
  });
  
  document.getElementById('stop').addEventListener('click', () => {
    clearInterval(interval);
  });
</script>

React Component

// React Timer Component
import { useState, useEffect } from 'react';

function CountdownTimer({ initialSeconds, onComplete }) {
  const [secondsLeft, setSecondsLeft] = useState(initialSeconds);
  const [isActive, setIsActive] = useState(false);
  
  useEffect(() => {
    let interval = null;
    
    if (isActive && secondsLeft > 0) {
      interval = setInterval(() => {
        setSecondsLeft(seconds => seconds - 1);
      }, 1000);
    } else if (secondsLeft === 0) {
      onComplete?.();
      setIsActive(false);
    }
    
    return () => {
      if (interval) clearInterval(interval);
    };
  }, [isActive, secondsLeft, onComplete]);
  
  const minutes = Math.floor(secondsLeft / 60);
  const seconds = secondsLeft % 60;
  
  // Announce at key moments
  const shouldAnnounce = secondsLeft === 60 || secondsLeft === 30 || secondsLeft === 10;
  
  return (
    <div>
      <div
        role="timer"
        aria-live={shouldAnnounce ? 'polite' : 'off'}
        aria-atomic="true"
        aria-label="Time Remaining"
      >
        {minutes}:{seconds.toString().padStart(2, '0')}
      </div>
      
      <button onClick={() => setIsActive(!isActive)}>
        {isActive ? 'Pause' : 'Start'}
      </button>
      <button onClick={() => setSecondsLeft(initialSeconds)}>
        Reset
      </button>
    </div>
  );
}

// Stopwatch Example
function Stopwatch() {
  const [elapsedMs, setElapsedMs] = useState(0);
  const [isRunning, setIsRunning] = useState(false);
  const [startTime, setStartTime] = useState(0);
  
  useEffect(() => {
    let interval;
    if (isRunning) {
      interval = setInterval(() => {
        setElapsedMs(Date.now() - startTime);
      }, 10);
    }
    return () => clearInterval(interval);
  }, [isRunning, startTime]);
  
  const hours = Math.floor(elapsedMs / 3600000);
  const minutes = Math.floor((elapsedMs % 3600000) / 60000);
  const seconds = Math.floor((elapsedMs % 60000) / 1000);
  
  return (
    <div
      role="timer"
      aria-label="Stopwatch"
      aria-live="off"
    >
      {hours.toString().padStart(2, '0')}:
      {minutes.toString().padStart(2, '0')}:
      {seconds.toString().padStart(2, '0')}
    </div>
  );
}

Best Practices

Keep aria-live="off" for continuous updates

Announce only at significant milestones (60s, 30s, 10s remaining)

Use aria-live="assertive" when timer reaches zero for urgent tasks

Provide clear aria-label (e.g., "Quiz Time Remaining")

Include time units in announcements for clarity

×

Don't announce every second - it's extremely annoying

×

Don't use for decorative animated counters

×

Don't forget to announce when timer completes

Specifications & Resources