Skip to content

HabMeter ​

A pill progress track whose color is derived from its value.

Coverage 100%
Coverage 82%
Coverage 51%
Coverage 18%
The color is derived from the value — this encodes “more is better”
18 / 100 seats
18 / 100 seats
18 / 100 seats
invert for a quantity where LESS is better — without it, 18% of seats used reads as an alarm
vue
<HabMeter :value="412" :max="480" label="Seats used" value-text="412 / 480" />

The color is an assertion ​

100% reads success, 70% and above reads accent, 40% and above reads warning, and below that reads error. That encodes "more is better" — right for coverage, wrong for consumption.

vue
<HabMeter :value="18" invert label="Seats used" value-text="18 / 100 seats" />

invert flips the scale for quota, spend and capacity. Without it, 18% of seats used renders as an alarm.

tone="neutral" drops the judgement altogether and fills with --hab-chart-muted. It is deliberately not the accent: the accent is also the "70% and above, healthy" band, so the tone that removes meaning would otherwise be the same pixel as one that asserts it.

color overrides the thresholds entirely, for a value whose color comes from elsewhere.

The readout, and the name ​

They are two different props, and the distinction is easy to get wrong.

label names what is being measured — Seats used — and is never rendered. It is the meter's accessible name, and nothing else.

valueText is the text beside the bar: prefer 412 / 480 over a percentage, since the raw numbers usually answer the next question too. It is also announced in place of the bare number, so a screen reader says "412 / 480" rather than "412". showPercent falls back to a rounded percentage when there is no valueText. Both readouts are tabular, so a column of meters aligns.

Passing the reading as label is the mistake to avoid — this page and every demo did it before the split, which named a control 412 / 480 and left nobody able to tell what it measured.

Accessibility ​

  • Carries a real role="meter" with aria-valuenow, aria-valuemin and aria-valuemax, so the value is announced rather than inferred from a bar's width. The design contract renders only the bar.
  • Give it a label, and make it a name rather than a reading. It becomes the meter's accessible name; without one the role is announced with a number and no indication of what is being measured, and the component warns. The reading goes in valueText, which is announced as aria-valuetext.
  • The color must not be the only thing carrying the judgement. "82%" and a warning fill say the same thing twice on purpose — a color-blind reader gets only the number.
  • The fill transitions on --hab-dur-value, the duration for something moving to a new value.

API ​

Props ​

PropTypeDefaultDescription
colorstringundefinedOverrides the threshold color entirely. Any CSS color.
heightnumber8Track height in px.
invertbooleanfalseFlips the scale for a quantity where less is better — seats used, quota spent, capacity. Without it, 20% of seats used renders as an alarm.
labelstringundefinedThe meter's accessible NAME — what is being measured. Seats used, Coverage, Storage. It is not rendered: a meter sits under a heading or beside a row that already names it on screen, and repeating that is noise. valueText is the visible half.
maxnumber100—
showPercentbooleanfalseShows a rounded percentage when there is no valueText.
tone"neutral"undefinedneutral drops the judgement: the fill becomes --hab-chart-muted and the value asserts nothing about whether it is good.
valuenumber0—
valueTextstringundefinedText to the right of the bar — the value in the caller's own units, e.g. 412 / 480. Also announced in place of the raw number. Falls back to a percentage when showPercent is set.

Proprietary — internal use only.