Meter
A meter is a measurement that happens to be somewhere in its range: disk used, budget spent, seats filled. It is not going anywhere — that is what separates it from a ProgressBar, which is a task on its way to finishing. The tell is that a meter has no indeterminate state; a quantity you cannot measure is not a quantity yet.
import { Label, Meter, MeterFill, MeterOutput, MeterTrack } from "ropav";The parts mirror the bar's: MeterTrack is the groove, MeterFill shows how much of it is taken, MeterOutput prints the value, and a Label says what is being measured.
<Meter class="w-80" :value="72">
<Label>Storage</Label>
<MeterOutput />
<MeterTrack>
<MeterFill />
</MeterTrack>
</Meter>Colour as a threshold
A meter is usually read at a glance, so the colour is doing the work: green while there is room, amber when it is getting tight, red when it is not. Derive it from the value rather than writing it per row, so the thresholds live in one place.
meter-thresholds.vue
<script setup lang="ts">
import { Label, Meter, MeterFill, MeterOutput, MeterTrack } from "ropav";
const disks = [
{ label: "Documents", value: 34 },
{ label: "Photos", value: 71 },
{ label: "System", value: 94 },
] as const;
// The colour is the reading, so it is derived from the value rather than written per row.
const colorFor = (value: number) => (value >= 90 ? "danger" : value >= 70 ? "warning" : "success");
</script>
<template>
<div class="stack">
<Meter
v-for="disk in disks"
:key="disk.label"
:color="colorFor(disk.value)"
:value="disk.value"
>
<Label>{{ disk.label }}</Label>
<MeterOutput />
<MeterTrack><MeterFill /></MeterTrack>
</Meter>
</div>
</template>
<style scoped>
.stack {
display: flex;
width: 100%;
max-width: var(--rp-container-md);
flex-direction: column;
gap: calc(var(--rp-spacing) * 6);
}
</style>Accessibility
- The root is a
role="meter", which every current browser and screen reader reads out as a level. It is a single role rather than ameter progressbarfallback list: axe does not read fallback lists, and flagged everyaria-value*attribute on one as not allowed. - Colour is a threshold and never the message. The output beside the label is what says 94%; the red says it faster to a reader who can see it, and nothing at all to one who cannot.
format-optionssets how the value reads. A percentage suits a proportion; bytes or a count suit a quantity the reader thinks about in those terms, andvalue-labelreplaces the text where neither fits.
API
Meter
| Prop | Type | Default | Description |
|---|---|---|---|
ariaDescribedby | string | — | |
ariaLabel | string | — | |
ariaLabelledby | string | — | |
color | "default" | "accent" | "success" | "warning" | "danger" | "accent" | Meter color. |
formatOptions | Intl.NumberFormatOptions | {style: "percent"} | Localized number formatting. |
id | string | — | |
maxValue | number | 100 | |
minValue | number | 0 | |
size | "xs" | "sm" | "md" | "lg" | "md" | Meter size. |
value | number | 0 | Current quantity. |
valueLabel | string | — | Explicit text announced and rendered for the current value. |
The default slot receives:
| Slot prop | Type |
|---|---|
percentage | number |
valueText | string | undefined |
MeterFill
| Prop | Type | Default | Description |
|---|---|---|---|
style | string | CSSProperties | — |
MeterOutput
Slots: default.
MeterTrack
Slots: default.