Meter
Display a measurement within a known range.
import { Div, Span } from "@implementjs/core";
import { Meter } from "@/lib/components/ui/meter";
export default function MeterDemo() {
const value = 3000;
const max = 4000;
return Div(
{ class: "flex w-full max-w-sm flex-col gap-2" },
Div(
{ class: "flex items-center justify-between text-sm font-medium" },
Span({ id: "tokens-label" }, "Tokens used"),
Span({ class: "tabular-nums" }, `${value} / ${max}`),
),
Meter({
"aria-labelledby": "tokens-label",
"aria-valuetext": `${value} of ${max} tokens used`,
value,
max,
}),
);
}A meter is a static measurement within a known range — CPU usage, battery level, a token quota. Meter renders a Div with role="meter" and the aria value attributes; you draw the track and the fill.
import { Meter } from "@implementjs/primitives";
Meter({ value: 75, "aria-label": "Storage used" });
It accepts optional props and children — pass a props object when you need attributes, or pass children directly. See createComponent. Extra props are forwarded onto the underlying Div.
A meter measures a current state relative to capacity; the value can move in either direction. If the value only ever advances toward completion — a file upload, a multi-step form — that is a progress bar, not a meter, and assistive technology announces the two differently.
Value and range
value defaults to 0 inside a range from min (0) to max (100). Pass numbers to seed them, or a signal to control them from outside (signal() returns a writable unchanged, so the same prop accepts both):
const usage = signal(40);
Meter({ value: usage, max: 200, "aria-label": "CPU usage" });
usage.set(80); // the aria and data attributes follow
Accessibility
The primitive sets role="meter", aria-valuemin, aria-valuemax, and aria-valuenow. Two things are left to you:
- A name. If there is a visible label, point
aria-labelledbyat itsid; otherwise passaria-label. - A readable value. Screen readers often announce
aria-valuenowas a percentage. When a percentage is not how a person would say the value, passaria-valuetext— a battery meter might use"50% (6 hours) remaining".
Span({ id: "battery-label" }, "Battery");
Meter({
"aria-labelledby": "battery-label",
"aria-valuetext": "50% (6 hours) remaining",
value: 50,
});
Styling
The primitive is invisible until you style it — it has no default size or color. Style the root as the track and put your own fill inside it; data-value, data-min, and data-max are on the root for CSS to react to:
Meter(
{
value: 75,
"aria-label": "Storage used",
class: "h-2 w-56 overflow-hidden rounded-full bg-muted",
},
Div({ class: "h-full bg-primary", style: { width: "75%" } }),
);
For a fill that follows a signal, bind the style:
const usage = signal(40);
Meter(
{
value: usage,
"aria-label": "CPU usage",
class: "h-2 w-56 overflow-hidden rounded-full bg-muted",
},
Div({ class: "h-full bg-primary", style: { width: usage.bind((v) => `${v}%`) } }),
);
API Reference
Meter
A static measurement within a known range. Sets role="meter" and the aria value attributes. Give it a track and a fill; it handles the semantics. Renders a Div; extra props are forwarded onto it.
| Prop | Type | Default | Description |
|---|---|---|---|
value | Signal<number> | number | 0 | The current value. Pass a signal to control it from outside; a number seeds uncontrolled state. |
min | Signal<number> | number | 0 | The lowest value the meter can take. |
max | Signal<number> | number | 100 | The highest value the meter can take. |
| Data attribute | Value |
|---|---|
[data-meter-root] | Present |
[data-value] | The current value |
[data-min] | The minimum value |
[data-max] | The maximum value |