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 optionallow,high,optimumto indicate good and bad ranges. Exposed as rolemeterin 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.
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.
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 | — |
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.
progressused 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.
Related
Permalink to "Related"- KPI cards and sparklines — unbounded metrics
- Progress indicators — task completion
- Non-text contrast for chart series — contrast for arcs and bars