Accessible Gauges and the meter Element

Permalink to "Accessible Gauges and the meter Element"

Dashboards are full of gauges: CPU at 72%, storage 8.4 of 10 GB, SLA 99.2% against a 99.5% target, budget 64% spent. Visually they are arcs, needles, rings and bars, often coloured green, amber and red. To screen readers, a styled div or an SVG arc is nothing; to colour-blind users, the threshold colours blur. HTML has a native element for exactly this — meter — and ARIA has a matching role. Used with a good name and value text, gauges become some of the simplest widgets to make accessible.

This guide belongs to chart type-specific patterns.

Spec reference

Permalink to "Spec reference"
  • HTML meter — a scalar measurement within a known range: value, min, max, and optional low, high, optimum to indicate good and bad ranges. Exposed as role meter in modern browsers.
  • ARIA role="meter" (ARIA 1.2) — for custom gauges: aria-valuenow, aria-valuemin, aria-valuemax, aria-valuetext, and a name.
  • progress — for task completion, not measurements. Storage used is a meter; an upload is progress.
  • SC 1.1.1 (A), SC 4.1.2 (A) — name and value programmatic.
  • SC 1.4.1 Use of Color (A) — “over threshold” must be stated, not only coloured red.
  • SC 1.4.11 Non-text Contrast (AA) — the arc or bar fill against its track at 3:1.
Styled div gauge versus meter Comparison: a styled div or SVG arc exposes nothing; a meter element with a name and value text announces the measurement, range and status. Styled div gauge versus meter✗ Styled div or SVG arcNothing announcedStatus by colour bandValue only as arc lengthNo range information✓ meter with name and valuetext"Storage used, meter, 8.4 of 10 GB, nearlimit"Status in wordsValue as visible text toomin, max, low, high, optimum

When to use — and when not to

Permalink to "When to use — and when not to"

Use meter (or role="meter") for any bounded measurement: utilisation, quota, score, budget consumption, SLA attainment. Show the number as text beside or inside the gauge.

Use progress for completion of a task. Use a plain number with a trend (a KPI card) when the range is not meaningful — revenue has no maximum.

The misapplication: an SVG ring gauge for every KPI on a dashboard, including unbounded ones, with colour bands as the only status indicator.

Quotas on a usage page Bar chart of quota usage: storage 84 percent near limit, API calls 46 percent, seats 100 percent at limit, and bandwidth 12 percent. Quotas on a usage pageStorage8.4 of 10 GB — near limitAPI calls460,000 of 1 M this monthSeats25 of 25 — at limitBandwidth120 of 1,000 GB
Each bar is a meter; status words travel with the values.

Annotated code example

Permalink to "Annotated code example"
<!-- Native meter: role, range and value exposed (SC 4.1.2) -->
<div class="gauge">
  <label for="storage-meter">Storage used</label>
  <meter id="storage-meter" min="0" max="10" low="7" high="9" optimum="0" value="8.4"
         aria-valuetext="8.4 of 10 GB, near limit">8.4 of 10 GB</meter>     <!-- text equivalent -->
  <span class="gauge-value">8.4 of 10 GB <strong>Near limit</strong></span>  <!-- status in words (SC 1.4.1) -->
</div>

<!-- Custom SVG ring with role="meter" -->
<div role="meter" aria-label="SLA attainment, September" aria-valuemin="95" aria-valuemax="100"
     aria-valuenow="99.2" aria-valuetext="99.2 percent, below 99.5 percent target">
  <svg aria-hidden="true" viewBox="0 0 120 120">…</svg>                      <!-- drawing hidden -->
  <span class="ring-label">99.2% <small>Target 99.5%</small></span>
</div>
function statusText(v, { low, high }) {
  return v >= high ? 'at or over limit' : v >= low ? 'near limit' : 'within limit';
}
meter.setAttribute('aria-valuetext', `${v} of ${max} GB, ${statusText(v, th)}`);

Keyboard & AT behaviour

Permalink to "Keyboard & AT behaviour"
Event Expected announcement AT deviations
Browse to native meter “Storage used, meter, 8.4 of 10 GB, near limit” Older readers may say “progress bar” or “level indicator”
role=“meter” gauge “SLA attainment, September, meter, 99.2 percent, below target” Support for role meter is good in current browsers
Value update Not announced automatically Announce threshold crossings separately
Forced colours Meter rendered with system colours; text status remains —
Choosing the element for gauge-like widgets Matrix of widget purposes — bounded measurement, task completion, unbounded KPI, user-adjustable value — against meter, progress, plain text and slider. Choosing the element for gauge-like widgetsmeterprogressText with trendsliderBounded measurementYesNoAlso fineNoTask completionNoYesNoNoUnbounded KPINoNoYesNoUser-adjustable valueNoNoNoYes
Only bounded measurements are meters.

Integration context

Permalink to "Integration context"

Gauges usually sit in dashboards alongside KPI cards — see accessible KPI cards and sparklines. Threshold colours follow chart colour & contrast. Progress for tasks is covered in progress indicators for long-running data operations. Threshold crossings in live dashboards are announced as in announcing dashboard refreshes.

Gotchas

Permalink to "Gotchas"

Styling meter. Native meters are hard to style consistently across browsers. Either accept limited styling, or use role="meter" on a custom element with the same attributes.

Inverted good ranges. For storage, low is good; for SLA, high is good. Set optimum accordingly and make the status words match.

Animated needles. Sweeping needle animations on load are decoration; respect prefers-reduced-motion.

Common mistakes

Permalink to "Common mistakes"
  • SVG arcs with no role or name.
  • Colour bands as the only status.
  • progress used for measurements.
  • Gauges for unbounded metrics.

Framework notes

Permalink to "Framework notes"

MUI LinearProgress and CircularProgress render progressbar roles — fine for tasks, wrong for measurements; use a native meter or a custom role="meter" for gauges. Angular Material has no meter component; build one with the attributes above. ECharts gauge series are canvas or SVG with no meter semantics — wrap them in a role="meter" container with values from the same data.

function Meter({ label, value, min = 0, max, valueText }) {
  return (<div role="meter" aria-label={label} aria-valuemin={min} aria-valuemax={max} aria-valuenow={value} aria-valuetext={valueText}>
    <GaugeArc value={value} min={min} max={max} aria-hidden="true" /><span>{valueText}</span>
  </div>);
}

Worked example

Permalink to "Worked example"

A SaaS admin’s usage page showed five ring gauges coloured green, amber and red, with percentages inside rings rendered as SVG text. A screen reader user heard five unlabelled graphics. The team replaced each with a horizontal bar using role="meter", a label (“API calls this month”), visible values (“460,000 of 1,000,000”) and a status word (“within limit”), with patterns for “near limit” and “at limit”. Unbounded metrics (active users) moved to KPI cards with trends. Usage questions to support dropped because the page now said, in words, which limits were close.

Testing checklist

Permalink to "Testing checklist"

Design system notes

Permalink to "Design system notes"

Provide a Meter component (bar and ring variants) with built-in status words derived from thresholds and optimum, and a lint rule against using progress components for measurements.

FAQ

Permalink to "FAQ"
Is the meter element well supported by screen readers?

In current browsers and screen readers, yes; some older combinations announce it as a progress bar. A good aria-valuetext makes the announcement useful either way.

Should gauges announce value changes?

Not automatically. Announce threshold crossings, which matter, rather than every change.

Can a gauge be a chart image with alt text?

For static reports, alt text with the value and status is acceptable. In live dashboards, use meter semantics so values stay current.

What about multi-threshold gauges?

Use low, high and optimum for three zones, and say the zone in words.

Should the ring itself be hidden from AT?

If the container has role=“meter” and value text, hide the drawing to avoid duplicate output.

How do I show targets?

In the value text (“below 99.5 percent target”) and visibly as a marker with a label.

How should a group of gauges be summarised?

Lead with exceptions: “2 of 5 quotas need attention: Storage near limit, Seats at limit.” Users can then read the individual meters for details.

Permalink to "Related"

← Back to Chart Type-Specific Patterns