Skip to content

Gauge

Shows one value against a maximum, as a dial, a ring, a bar or a row of dots. Optional ranges color the scale.

72%At risk
Quota attainment

Installation

pnpm dlx shadcn@latest add @wisty/gauge

The CLI installs the Radix or Base UI file to match your project.

If your project doesn't have Wisty UI's theme yet, add it once with npx shadcn@latest add @wisty/cosmos. Until then, components keep shadcn's default look. Installation has the details.

When to use

Use it

  • One value is measured against a scale or a goal, like quota attainment, win rate or customer satisfaction.
  • A value falls into ranges that mean something, like behind, at risk and on track. The default dial draws them on its scale, as a Lightning dashboard's Gauge does.
  • Progress toward a goal, where max is the goal. Use a ring (variant="polar"), or a bar (variant="flat") where space is tight.
  • A score out of a few points, like a 1 to 10 survey. Use variant="rating".
  • A usage meter against its limit, like data storage. Use a bar with the limit as max, as the Against a limit example shows.

Don't use it

  • For a number with no maximum or scale. Use Metric.
  • For a task's progress, like an upload or an import. Use Progress.
  • For several values to compare. Use Chart, with a bar or column chart.
  • For a record's stage in a process. Use Path.
  • For a rating someone enters. The rating variant only shows a score. Use Rating.

Usage

import { Gauge } from "@/components/wisty/gauge"
<Gauge
  variant="flat"
  label="First contact resolution"
  value={64}
  unit="%"
  reference={70}
/>

Pick the variant by what the reader compares the value with. A dial shows where the value sits on ranges from bad to good. A ring and a bar show progress toward max, the goal. A rating shows a score on max dots, five by default.

Add a reference when there's a point to compare with, like a target or the team's average, and name it with referenceLabel ("Target" by default, "Avg" for an average). It's optional on every variant.

A band runs up to its to, not including it, so a value of exactly 80 falls in the band after to: 80. A value at or past the last band's to counts as the last band. Leave out a band's tone for a calm range below a warning. Format the value with valueFormatter for the org's locale; the examples use en-US.

Examples

Ring

Metric. 75% of the goal on a ring from `variant="polar"`, with the value in the middle.

75%
Progress toward quota

Flat

Metric. Two bars from `variant="flat"`, each with the team average as a `reference` named Avg.

Quota attainment
Avg 76%
98%
Win rate
Avg 82%
75%

Rating

Metric. Customer satisfaction of 7 on ten dots from `variant="rating"`, against a target `reference` of 8.

Customer satisfaction
Target 8
7

Without bands

Metric. A dial with no `bands`: a win rate of 34% fills the track up to the needle, with the 40% average as a `reference`.

34%Avg 40%
Win rate

Against a limit

Metric. Data storage at 4.6 of a 5 GB `max` on a bar, in a Near limit band after a band with no tone.

Data storageNear limit
4.6 GB

Bands

Lightning's segment ranges from `bands` on a ring for the team and a bar per rep: behind below 50%, at risk below 80%, on track from it.

68%At risk
Team quota
Ana SilvaBehind
42%
Ben ItoAt risk
71%
Cara DiazOn track
93%

Over target

Metric. A `value` of 124 past a `max` of 100, as a dial and a bar: the needle and the bar stop at max and the text shows the real value.

124%
Progress toward quota
Progress toward quota
124%

No data

Metric. A `value` of `null` for attainment that didn't load: the dial has no needle, the bar is empty, and both say No data.

No data
Progress toward quota
Progress toward quota
No data

API reference

Gauge

PropTypeDefaultDescription
label*React.ReactNodeNames the gauge. Shown under a dial or a ring, or above a bar or a rating.
value*number | nullThe value. It may pass max; the drawing stops at max, the text doesn't. null shows noDataLabel.
maxnumberThe end of the scale, which starts at 0: the goal on a ring or a bar, the number of dots on a rating. Default 100, or 5 on a rating.
variant"dial" | "polar" | "flat" | "rating""dial"A half dial with a needle (dial), a ring (polar), a bar (flat) or a row of dots (rating).
referencenumberA point to compare the value with, like a target or an average. Keep it within max, or it has no mark.
referenceLabelstring"Target"The reference's name, shown with it, like "Target" or "Avg".
bandsGaugeBand[]Value ranges, lowest first. A dial draws them on its scale; the other variants take the color of the one the value is in.
noDataLabelstring"No data"Shown and read when value is null, or max isn't above 0.
valueFormatter(value: number) => string(value: number) => oneDecimal.format(value)Formats the value, the reference and a dial's scale. Default rounded to one decimal.
unitstringA unit after the value and the reference, like "%" or "days".

Also takes every <div> (opens in a new tab) prop.

GaugeBandtype

type GaugeBand =
  | {
      to: number
      tone: "success" | "warning" | "error"
      label: string
    }
  | {
      to: number
      tone?: undefined
      label?: string
    }

SLDS mapping

SLDS has no gauge component. The dial is Lightning's dashboard Gauge: a half ring with its segment ranges on the scale, the scale's values inside it, and a needle at the value. The ring, the bar and the rating are the polar gauge, flat gauge and ratings chart of the SLDS Charts guidelines' Metric intent: the ring and the bar fill toward max, the goal, and the guidelines ask for a reference point, a target or an average, where there is one. bands take the colors the charts and Metric's trend use; on a ring, a bar or a rating they color the value instead of the scale.

Known gaps:

  • Lightning's dial shows no value by default and has no reference. This one writes the value and the band's name under the hub, and marks a reference when you pass one.
  • Lightning draws its gauge in SLDS 1's colors. This one uses SLDS 2's success, warning and error colors, so it changes with the theme.
  • The guidelines say a ring can drain as well as fill. Pass what's left as the value to drain one.
PropValueSLDSNotes
variantdial (added)none (Lightning's dashboard Gauge)A half dial with its ranges on the scale and a needle at the value, with the value under it. The default.
variantpolar (added)none (the Charts guidelines' polar gauge)A ring that fills clockwise from 12 o'clock toward max, the goal, with the value in the middle.
variantflat (added)none (the Charts guidelines' flat gauge)A thick bar toward max, the goal, with the label above and the value at its end. Use it where space is tight.
variantrating (added)none (the Charts guidelines' ratings chart)A row of max dots, filled up to the value, with the label above and the value at its end.
referencenumber (added)noneOptional: a target or an average to compare with, named by referenceLabel. A tick across the dial or the ring, or a dashed line across the bar or the dots with its name in a tag below.
bandssuccess, warning, error (added)slds-progress-ring_complete, _warning, _expiredLightning's segment ranges. A dial draws them on its scale; the other variants take the color of the band the value is in. Either way the band's name shows in its color. A band without a tone is the track's color on a dial and keeps the default color elsewhere.

Accessibility

  • The gauge is a meter named by its label. It sets its own role and aria-value* attributes after your props, so you can't replace them.
  • aria-valuenow stops at max, as a meter's value must. aria-valuetext carries the real value, the band and the reference, like "68%, At risk, Avg 80%".
  • The drawing is hidden from screen readers, and so are the dial's scale values. Everything it shows is also in the text.

Motion

  • The dial's needle sweeps from 0 to the value over 800ms when it first draws, with the charts' easing, and to a new value when the value changes. A dial without bands fills its track along with it.
  • With reduced motion, the needle points straight at the value.
  • The ring, the bar and the rating don't animate. A new value jumps into place.

Responsive behavior

  • A dial is as wide as its parent, up to 18rem, and a little over half as tall. Below 12rem its scale shows only 0 and max, with no minor ticks.
  • A ring is as wide as its parent, up to 14rem, and as tall as it is wide. Narrow either with className, like max-w-48.
  • The ring scales but its text doesn't. Below about 160px the value no longer fits inside, so use a bar there and in lists of people or records.
  • A bar fills its parent, with the value at its end. A rating's dots keep their size and shrink only when the row is too narrow.
  • In a list of bars whose values differ in width, give the value a fixed width so the bars line up, like [&_[data-slot=gauge-value]]:min-w-32.

Rules

  • Pass the real value, even past max. The drawing stops full and the text shows the real value, like "124%". Never clamp or round it yourself.
  • Pass null for a value that didn't load, never 0, so the gauge says "No data" (the No data example).
  • Keep reference within max: a reference past it gets no mark, only its text.
  • On a rating, keep max to ten dots or fewer, and pass a whole-number reference so its line falls between dots.
  • On a dial, a band without a tone draws in the track's color. Use it for a calm range, never for one that needs attention.
  • Pass valueFormatter a formatter with a fixed locale, like (value) => compact.format(value), so server and client print the same text.
  • Change the default value color through className, like [--gauge-color:var(--chart-3)]. It colors a dial's fill when it has no bands, and the value on the other variants. Bands override it.
  • Never pass asChild or render.