Progress and Meter

<progress> and <meter> both draw a horizontal bar, but they mean different things and screen readers announce them differently (progressbar versus meter role). The HTML standard is explicit: use <progress> for the completion of a task and <meter> for a measurement within a known range, also called a gauge.

The hints split the range into low, middle and high regions, and optimum says which one is good. If optimum lies below low, low values are green, middle values yellow and high values red; above high, the colors reverse; and between low and high, the middle is green and both ends are yellow.

Progress bars and gauges in their different statesHTML
<!doctype html>
<html lang="en">
  <meta charset="utf-8">
  <style>
    body { font: 15px/1.4 system-ui, sans-serif; margin: 10px; }
    label { display: flex; justify-content: space-between; width: 520px; margin: 6px 0; }
    progress, meter { width: 260px; }
  </style>
  <label>Uploading photo.jpg <progress id="up" max="100" value="0">0%</progress></label>
  <label>Waiting for server <progress>Working...</progress></label>
  <!-- Disk: lower is better, so optimum sits below low -->
  <label>Disk 20 GB <meter max="100" low="60" high="85" optimum="10" value="20"></meter></label>
  <label>Disk 70 GB <meter max="100" low="60" high="85" optimum="10" value="70"></meter></label>
  <label>Disk 95 GB <meter max="100" low="60" high="85" optimum="10" value="95"></meter></label>
  <!-- Battery: higher is better, so optimum sits above high -->
  <label>Battery 15% <meter low="0.2" high="0.8" optimum="1" value="0.15">15%</meter></label>
  <label>Battery 90% <meter low="0.2" high="0.8" optimum="1" value="0.9">90%</meter></label>
  <script>
    const bar = document.getElementById('up');
    const timer = setInterval(() => {
      bar.value += 10;                     // a real upload would use progress events
      if (bar.value >= 60) clearInterval(timer);
    }, 100);
  </script>
Browser output of Listing 2.18
Browser output of 18

The script stops the upload bar at 60% so the capture shows a determinate state. The disk meters turn green, yellow and red as usage grows, while the battery meter is red when low and green when full: same element, opposite optimum. With real data, update value from an XMLHttpRequest progress event or a fetch() stream reader.

Practical advice