Meter
Show a bounded scalar value, with explicit units and range rather than a claim that a task is underway.
Choose a scalar measurement
Use Meter for a current quantity within a meaningful known range, such as storage consumption. Use Progress for task completion. Meter has no indeterminate state: absent measurement needs an explicit unavailable message, not an invented zero.
A percentage graphic does not prove confidence, linguistic quality, consent or learning mastery. Name what was measured, where it came from and the units when applying a meter to a real product.
Anatomy and specification
| Part | Contract |
|---|---|
| Root | Named meter semantics from installed Base UI |
| Track | Full container width; 8px height; --color-muted / --radius-full |
| Indicator | Measured fraction; --color-primary |
| Text | Visible value/units and meaningful value text |
| Update | Width transition; removed with reduced motion |
| Bounds | Finite min < max; validate incoming application data |
Working measurement
4 of 20 sample units used. This scalar measurement is not task progress, confidence or language proficiency.
'use client';
import { useState } from 'react';
import { Button, CircularProgress, Meter, Progress } from '@mobtranslate/ui';
export function ProgressCommunicationExample() {
const [value, setValue] = useState<number | null>(null);
const [size, setSize] = useState<'sm' | 'md' | 'lg'>('md');
const text = value === null ? 'Local task underway; total unknown.' : `${value} of 10 local steps complete.`;
return <div data-progress-lab className="space-y-4">
<p>No timer invents progress. Choose measured local steps or an unknown total explicitly.</p>
<label>Circular size <select value={size} onChange={event => setSize(event.target.value as typeof size)} className="mt-input"><option value="sm">sm · 20px</option><option value="md">md · 32px</option><option value="lg">lg · 48px</option></select></label>
<Progress className={state => `lab-progress-${state.status}`} value={value} min={0} max={10} aria-label="Local task progress" getAriaValueText={() => text} />
<CircularProgress className={state => `lab-circular-${state.status}`} size={size} value={value} min={0} max={10} aria-label="Circular local task progress" getAriaValueText={() => text} />
<div className="flex flex-wrap gap-3">
<Button type="button" variant="secondary" onClick={() => setValue(current => Math.min(10, (current ?? 0) + 1))}>Complete one local step</Button>
<Button type="button" variant="outline" onClick={() => setValue(0)}>Reset measured progress</Button>
<Button type="button" variant="ghost" onClick={() => setValue(null)}>Use unknown total</Button>
</div>
<p role="status" aria-atomic="true">{text}</p>
</div>;
}
export function MeterCommunicationExample() {
const [value, setValue] = useState(4);
return <div data-meter-lab className="space-y-4">
<label className="block">Local storage sample <input type="range" min="0" max="20" value={value} onChange={event => setValue(Number(event.target.value))} className="block w-full" /></label>
<Meter value={value} min={0} max={20} aria-label="Local storage sample" getAriaValueText={() => `${value} of 20 sample units used`} />
<p>{value} of 20 sample units used. This scalar measurement is not task progress, confidence or language proficiency.</p>
</div>;
}
API reference
MeterProps derives from the installed Meter.Root. value is a required number; getAriaValueText receives a formatted string and numeric value. There is no timeout, task status or built-in threshold-warning API in this wrapper.
| Prop | Type / default | Contract |
|---|---|---|
| value | number | null for Progress; number for Meter | Supply an explicit value; null means an unknown task total, not zero. |
| min / max | number; 0 / 100 | Finite range with min < max; validate application measurements before rendering. |
| aria-label / aria-labelledby | Native ARIA props | Name the task or measurement; the colored track is not a label. |
| getAriaValueText | Callback(formattedValue, value) → string | Describe meaningful units. Progress callback arguments can be null. |
| aria-valuetext | string | Explicit alternative when application copy supplies the value. |
| locale / format | Intl locale and NumberFormat options | Formatting does not translate content or establish its provenance. |
| className | string or root state callback | Merged with package classes; Progress state has status. |
| style / render / native props / ref | Installed Base UI root contract; HTMLDivElement ref | Custom rendering must preserve forwarded semantics and ref. |
| children | Wrapper-owned structure | These wrappers render their own track/indicator; place labels and outcomes adjacent. |
Behavior and accessibility
- The meter itself is read-only and is not a slider. A separate named input controls the demonstration.
- Keep min, max, current value and units consistent. If units or bounds change, update visible and accessible text together.
- Do not infer warning thresholds from the color. If a real product has a threshold, explain its source and the consequence.
- Use a contextual status message for a meaningful change; a meter does not itself guarantee a spoken update.
Failure cases
- Unknown data rendered as zero.
- A loading spinner or completion animation attached to a static measurement.
- A hidden units/range label or a qualitative score with no measured basis.
- Assuming the wrapper supports low/high/optimum or threshold coloring because native HTML meter does.
Verification and release scope
- Operate the checked example with keyboard and pointer; inspect the actual name, role, relationships and focus destination.
- Check both themes, narrow reflow, long labels, reduced motion and forced colors. Text contrast and meaningful graphic contrast are separate checks.
- Test the relevant waiting, completion, failure, cancellation and recovery paths. A local simulation is not a verified backend result.
- Use a named screen-reader/browser pair to verify spoken output. DOM attributes and automated passes do not prove the announcement.
- Retain source, review and permission evidence when applying these controls to a real language workflow. Color and motion cannot establish authority.