Skip to content

Meter

  • Disk or quota usage
  • Password strength
  • A score or rating within a known range

Not for progress through a task — use a Progress.

Choosing between showing progress, loading or a measurement:

ComponentUse it forFor example
ProgressA task whose completion is known, moving toward doneUpload at 40%, step 2 of 5
StepperThe named steps of a multi-step flow, and which one you’re onCheckout: Cart, Shipping, Payment
MeterA measurement within a known range, not a taskDisk usage, password strength, a score
SpinnerWork of unknown length, in a small spaceA button saying “Saving…”, a panel refreshing
SkeletonContent still loading whose shape is known, so the layout does not jumpPlaceholder rows in a list, a profile card
import { Meter } from '@arun-dev/ui';
const stack = { display: 'grid', gap: 'var(--space-md)', maxInlineSize: '20rem' };
const field = { display: 'grid', gap: 'var(--space-3xs)' };
// Disk usage: low is good, so optimum sits low. Past `high` it turns red.
const disks = [
{ name: 'Photos', used: 0.3 },
{ name: 'Projects', used: 0.68 },
{ name: 'Backups', used: 0.92 },
];
export default function MeterBasics() {
return (
<div style={stack}>
{disks.map((disk) => (
<div key={disk.name} style={field}>
<label htmlFor={`meter-${disk.name}`}>
{disk.name}: {Math.round(disk.used * 100)}% used
</label>
<Meter id={`meter-${disk.name}`} value={disk.used} low={0.6} high={0.85} optimum={0.1} />
</div>
))}
</div>
);
}

Meter is a native <meter>, for a measurement within a known range, such as disk usage or a password’s strength. low, high and optimum colour it as the browser does:

RegionColourToken
the one optimum is ingood--meter-optimum-color
next to itfair--meter-suboptimum-color
the far sidepoor--meter-poor-color

In the example, low usage is best (optimum is low), so usage past high turns red.

The colours aren’t tied to high or low values. optimum says which end is good, and the colours follow it:

MeasurementGood whenSet optimum90% shows
Disk usagelowbelow low, e.g. 0.1red
Average scorehighabove high, e.g. 1green
import { Meter } from '@arun-dev/ui';
const stack = { display: 'grid', gap: 'var(--space-md)', maxInlineSize: '20rem' };
const field = { display: 'grid', gap: 'var(--space-3xs)' };
// Test scores: high is good, so optimum sits high. Below `low` it turns red.
const classes = [
{ name: 'Class A', average: 0.9 },
{ name: 'Class B', average: 0.62 },
{ name: 'Class C', average: 0.35 },
];
export default function MeterScores() {
return (
<div style={stack}>
{classes.map((group) => (
<div key={group.name} style={field}>
<label htmlFor={`score-${group.name}`}>
{group.name}: {Math.round(group.average * 100)}% average
</label>
<Meter
id={`score-${group.name}`}
value={group.average}
low={0.5}
high={0.75}
optimum={1}
/>
</div>
))}
</div>
);
}

Here a high score is best (optimum is 1), so 90% is green, and an average under low turns red. With optimum between low and high, the middle is good and both ends are fair; neither end turns red.

Meter props
PropTypeDefaultDescription
valuenumberThe measurement, between min and max.
minnumber0The bottom of the range.
maxnumber1The top of the range.
lownumberBelow this is the low region. With high and optimum, it picks the colour.
highnumberAbove this is the high region.
optimumnumberThe best value. Its region is good (success); the next is fair (warning); the far one is poor (error).
classNamestringConcatenated with the component's own classes, never replacing them.
renderReactElementElement or component to render instead of the default. Props, className, event handlers and ref are merged onto it.
refRef<HTMLElement>Ref to the rendered element. Merged with any ref on the render element.
…restHTMLAttributesAnything else is spread onto the rendered element, so id, aria-*, data-* and event handlers all reach the DOM.
  • Progress through a task is a Progress.
  • Name it with a <label> or aria-label, and put the value in the text.