Skip to content
omaris

Data Display

Gauge

A radial gauge — one number against the range it lives in.

import { Gauge } from 'omaris'
Learn

Examples

Basic

One number against the range it lives in. label goes under the readout.

0 CPU
0 Memory
0 Disk
<script lang="ts">	import { Gauge } from 'omaris';</script>​<Gauge value={72} label="CPU" /><Gauge value={38} label="Memory" tone="info" /><Gauge value={94} label="Disk" tone="destructive" />

Segments and ticks

segments paint zones on the track — a red band past 80% — without touching the indicator, so the reading and the scale stay separate things. ticks marks the scale, and format writes the number in the middle.

0 Load
0 of 2,000
<script lang="ts">	import { Gauge, latn } from 'omaris';</script>​<Gauge	value={86}	label="Load"	tone="warning"	ticks={9}	segments={[		{ from: 0, to: 60, tone: 'success' },		{ from: 60, to: 80, tone: 'warning' },		{ from: 80, tone: 'destructive' }	]}/>​<Gauge	value={1420}	max={2000}	label="of 2,000"	tone="tertiary"	format={(v) => Math.round(v).toLocaleString(undefined, latn())}/>

Sizes and sweep

sweep is degrees of arc: 270 is the classic dial, 360 closes it into a ring. thickness is a share of the radius, so it holds at every size.

0 sm
sweep 270
0 md
sweep 360
0 lg
sweep 180
<script lang="ts">	import { Gauge, Text } from 'omaris';</script>​<div class="flex flex-wrap items-end justify-center gap-8">	<div class="flex flex-col items-center gap-2">		<Gauge value={64} size="sm" label="sm" />		<Text variant="label-sm" tone="muted" font="mono">sweep 270</Text>	</div>​	<div class="flex flex-col items-center gap-2">		<Gauge value={64} size="md" sweep={360} rounded={false} label="md" />		<Text variant="label-sm" tone="muted" font="mono">sweep 360</Text>	</div>​	<div class="flex flex-col items-center gap-2">		<Gauge value={64} size="lg" sweep={180} thickness={0.32} label="lg" tone="secondary" />		<Text variant="label-sm" tone="muted" font="mono">sweep 180</Text>	</div></div>

Animated

A new value glides in on the emphasized curve, and the number counts with the arc — both driven by one tween, so they stop together. animate={false} makes it jump; a number sets the duration.

0 Throughput
0% Ring
<script lang="ts">	import { Button, Gauge, SegmentedButton } from 'omaris';​	let value = $state(64);	let mode = $state('smooth');​	const animate = $derived(mode === 'instant' ? false : mode === 'slow' ? 1600 : true);​	const bump = () => (value = Math.round(5 + Math.random() * 90));</script>​<div class="flex flex-col items-center gap-4">	<div class="flex flex-wrap items-center justify-center gap-6">		<Gauge {value} {animate} label="Throughput" tone={value > 80 ? 'warning' : 'primary'} />		<Gauge			{value}			{animate}			label="Ring"			sweep={360}			thickness={0.14}			tone="tertiary"			format={(v) => `${Math.round(v)}%`}		/>	</div>	<div class="flex flex-wrap items-center justify-center gap-3">		<SegmentedButton			bind:value={mode}			size="sm"			label="Animation"			items={[				{ value: 'smooth', label: 'Smooth' },				{ value: 'slow', label: 'Slow' },				{ value: 'instant', label: 'Instant' }			]}		/>		<Button size="sm" variant="tonal" onclick={bump}>New reading</Button>	</div></div>

When to use it

Use it for

  • One number against its range: CPU at 72%, quota used, a score out of 100. The value is printed in the middle, so nobody has to read an arc.
  • A status light with a number in it. Set tone="warning" past 60 and destructive past 80 from the value in the page.
  • A scale with zones. segments paint a red band from 80 on the track without touching the indicator, so reading and scale stay separate.
  • A live reading. The arc and the number tween together, so a new value glides and counts up. animate={false} lands a value at once.

Not for

  • Progress toward finishing a task, like an upload or a wizard → Progress. A gauge is a reading, not a wait.
  • Several progress-shaped values side by side → RadialBarChart in Chart, and past about six a BarChart.
  • A trend over time → Sparkline inside the stat, or a LineChart, both in Chart.
  • A number with no meaningful ceiling, like revenue or a count → a display-sm Text in a card. A dial with an invented max reads as a share that is not there.

Do

  • Say what the number is under it: label="CPU", or label="of 2,000" when format writes the unit into the readout.
  • Pick sweep for the space: 270 is the classic dial, 180 fits a half-height card, 360 closes it into a ring.
  • In a dense table cell, use size="sm" with showValue={false} and put the figure in the next column.
  • Prefer this one, from omaris, unless the page already imports omaris/chart. That entry has a Gauge too, drawn by layerchart with thresholds and caption, for dials that share the chart palette.

Don't

  • Colour the indicator from a segments band and from tone with two different meanings. The band is the scale; the tone is the reading.
  • Set ticks on a sm gauge. At that size they are noise.
  • Tween a value that updates several times a second. Pass a short animate or false, or the dial never catches up.

Quick reference

tone
  • primary (default)
  • secondary
  • tertiary
  • destructive
  • success
  • warning
  • info
size
  • sm
  • md (default)
  • lg

API

Gauge

A radial gauge — one number against the range it lives in.

The arc is a real SVG stroke with a dash offset, and it stays crisp at any size. The sweep and the number in the middle are driven by one tweened value on the emphasized curve, so a new reading glides in and counts up together, stopping at the same instant — and the dial rises from empty when it first appears. animate={false} makes it jump, a number sets the duration in ms. segments paint zones on the track (a red band past 80%, say) without touching the indicator, so the reading and the scale stay separate things.

import { Gauge } from 'omaris'
<Gauge value={72} label="CPU" tone="warning" segments={[{ from: 80, tone: 'destructive' }]} />

Parts

Every element this renders is reachable from outside. Pass Tailwind for one part as classes={{ part: '…' }}; class covers the root and is merged last, so it always wins.

root
No description in the source yet.
svg
The square the arc is drawn in.
track
The unfilled remainder of the sweep.
segment
A coloured band on the track, behind the indicator.
indicator
The filled arc. Its sweep is tweened in script; only the colour transitions here.
content
Centred stack: value over label.
value
No description in the source yet.
label
No description in the source yet.
tick
Tick marks around the scale.

Props

value required
number
min

Defaults to 0

number
max

Defaults to 100

number
tone

Defaults to 'primary'

GaugeTone
primary
secondary
tertiary
destructive
success
warning
info
size

Defaults to 'md'

GaugeSize
sm
md
lg
sweep

Defaults to 270

number

Degrees of arc. 270 is the classic dial; 360 closes it into a ring.

thickness

Defaults to 0.22

number

Stroke width as a share of the radius.

rounded

Defaults to true

boolean

Round the ends of both arcs.

segments
GaugeSegment[]

Zones on the track — thresholds, safe ranges, a red line.

ticks

Defaults to 0

number

Evenly spaced ticks around the scale. 0 draws none.

label
string

Under the number — "CPU", "of 2,000".

format
(value: number) => string

Formats the number in the middle. Defaults to a rounded integer.

showValue

Defaults to true

boolean

Hide the centred readout — for a gauge in a dense table cell.

animate

Defaults to true

boolean | number

Glide to a new value rather than jump. true takes 700 ms on the emphasized curve, a number sets the duration in ms, false is instant. Reduced motion is always instant.

children
Snippet

Replaces the whole centre stack.

name
string

Accessible name. Falls back to label, and one of the two is required: a meter with a value and no name reaches a screen reader as "64 percent" of nothing. Give it one whenever the centre stack is replaced by children, which is the case where label is gone.

class
string
classes
GaugeClasses
height
number | string

Plot height. A number is px; a string is any CSS length.

color
string

Any CSS colour. Ignored where a thresholds entry matches.

thresholds
{ at: number; color: string }[]

Colour by value: the first entry whose at the value is at or under wins, and anything above them all falls back to color.

caption
string

A word under the number — "of quota", "requests/s".

centre
Snippet<[{ value: number }]>

Replaces the number and caption in the middle.

title
string

Accessible description of what the gauge shows.

error
boolean | string | Error

Something went wrong. Truthy puts the chart into its error state; a string or an Error supplies the message.

errorText
string

The message, when error is just true.

errorState
Snippet<[{ message: string }]>

Replaces the whole error state.

onRetry
() => void

Offers a "Try again" button in the error state.

csv
boolean | string

Offer the plotted rows as a CSV download, from a button in the corner. A string names the file.

csvColumns
(string | { key: string; label?: string; value?: (row: T) => unknown })[]

Which fields the download writes, in order. Defaults to every field.