MobTranslate styleguide

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

PartContract
RootNamed meter semantics from installed Base UI
TrackFull container width; 8px height; --color-muted / --radius-full
IndicatorMeasured fraction; --color-primary
TextVisible value/units and meaningful value text
UpdateWidth transition; removed with reduced motion
BoundsFinite 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.

tsx
'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.

PropType / defaultContract
valuenumber | null for Progress; number for MeterSupply an explicit value; null means an unknown task total, not zero.
min / maxnumber; 0 / 100Finite range with min < max; validate application measurements before rendering.
aria-label / aria-labelledbyNative ARIA propsName the task or measurement; the colored track is not a label.
getAriaValueTextCallback(formattedValue, value) → stringDescribe meaningful units. Progress callback arguments can be null.
aria-valuetextstringExplicit alternative when application copy supplies the value.
locale / formatIntl locale and NumberFormat optionsFormatting does not translate content or establish its provenance.
classNamestring or root state callbackMerged with package classes; Progress state has status.
style / render / native props / refInstalled Base UI root contract; HTMLDivElement refCustom rendering must preserve forwarded semantics and ref.
childrenWrapper-owned structureThese 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.