# Gauge

A radial gauge of dithered blocks for a single reading, such as load, usage or a score, with a solid block at the value.

> For the complete documentation index, see [llms.txt](/llms.txt). Markdown variants are available by appending `.md` to any URL or sending an `Accept: text/markdown` header. The dithercn agent skill, for building any dither on the engine, is at [/.well-known/agent-skills/dithercn/SKILL.md](/.well-known/agent-skills/dithercn/SKILL.md).

> **This is an example, not the only option.** It is one component built on the dithercn engine. Install it when it is what you were asked for. For any other look, don't stretch its props to fit: write your own field on `DitherShader` ([Write your own dither](/docs/engine/write-your-own.md)), and read this example's source for technique. Its full source is in [https://dithercn.dev/r/gauge.json](https://dithercn.dev/r/gauge.json), under `files[].content`.

## Installation [#installation]

```bash
npx shadcn@latest add https://dithercn.dev/r/gauge.json
```

## Usage [#usage]

```tsx
import { Gauge } from "@/components/dithercn/gauge";

export function CpuGauge({ load }: { load: number }) {
  return <Gauge value={load} label="CPU load" unit="%" />;
}
```

The arc is split into blocks over a faint track in the same colour. Blocks up to the value fill in, and the block at the value is solid and steps out by one cell, so the arc points at the reading like a needle. With `fill="gradient"` each block behind it is a step lighter; `dotted`, `hatched` and `solid` fill evenly.

## Examples [#examples]

### Dials on a host card [#dials-on-a-host-card]

`variant="dial"` sweeps 270°, like a speedometer. A gauge over 90% switches to `color="red"` with `fill="hatched"` and a low bloom, so it reads as a warning without relying on colour alone.

```tsx title="examples/gauge-dial.tsx"
import { Gauge } from "@/components/dithercn/gauge";

const HOSTS = [
  { label: "CPU", value: 42 },
  { label: "Memory", value: 71 },
  { label: "Disk", value: 93 },
] as const;

export default function GaugeDial() {
  return (
    <div className="bg-background border-border w-full max-w-xl rounded-2xl border p-5 [--dither-paper:var(--background)] sm:p-6">
      <div className="flex items-baseline justify-between gap-4">
        <span className="font-medium">db-primary-01</span>
        <span className="text-muted-foreground font-mono text-xs">
          eu-west-1
        </span>
      </div>
      <div className="mt-6 flex flex-wrap justify-around gap-6">
        {HOSTS.map(({ label, value }) => (
          <Gauge
            key={label}
            variant="dial"
            value={value}
            label={label}
            unit="%"
            size={136}
            color={value > 90 ? "red" : "blue"}
            fill={value > 90 ? "hatched" : "gradient"}
            bloom={value > 90 ? "low" : "off"}
          />
        ))}
      </div>
    </div>
  );
}
```

### Ring beside a summary [#ring-beside-a-summary]

`variant="ring"` is a full circle starting at the top, with the reading in the middle, here in `green` with `fill="dotted"`. It hides the min and max labels, which would sit on top of each other.

```tsx title="examples/gauge-ring.tsx"
import { Gauge } from "@/components/dithercn/gauge";

export default function GaugeRing() {
  return (
    <div className="bg-background border-border flex w-full max-w-md flex-wrap items-center gap-6 rounded-2xl border p-5 [--dither-paper:var(--background)]">
      <Gauge
        variant="ring"
        value={63}
        label="budget left"
        unit="%"
        size={148}
        color="green"
        fill="dotted"
      />
      <div className="flex min-w-40 flex-1 flex-col gap-1.5">
        <span className="font-medium">Checkout API error budget</span>
        <span className="text-muted-foreground text-sm text-pretty">
          SLO 99.9% over 30 days. 16 of 43 minutes used.
        </span>
        <span className="text-muted-foreground mt-2 font-mono text-xs">
          Burn rate 0.8×
        </span>
      </div>
    </div>
  );
}
```

## Props [#props]

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` (required) | `number` |  | The reading. Values outside min and max are clamped. |
| `label` (required) | `string` |  | What is measured. Shown under the value and used as the accessible name. |
| `min` | `number` | `0` | The start of the scale. |
| `max` | `number` | `100` | The end of the scale. |
| `variant` | `"half" \| "dial" \| "ring"` | `"half"` | A 180° arc, a 270° dial, or a full ring. |
| `size` | `number` | `220` | Width in CSS pixels. Text and ring thickness scale with it. |
| `segments` | `number` | `28, 36 or 48` | How many blocks the arc is split into. The default depends on the variant. |
| `unit` | `string` | `""` | Shown after the value, such as "%". |
| `format` | `Intl.NumberFormatOptions` | `{ maximumFractionDigits: 1 }` | How the value, min and max are formatted. |
| `showRange` | `boolean` | `true, false for ring` | Show min and max under the ends of the arc. |
| `color` | `DitherColor` | `"ink"` | Colour of the filled blocks, a palette name or any CSS colour. |
| `trackColor` | `DitherColor` | `color` | Colour of the empty track, drawn faint. |
| `fill` | `"gradient" \| "dotted" \| "hatched" \| "solid"` | `"gradient"` | Texture of the filled blocks. The block at the value is always solid. |
| `bloom` | `"off" \| "low" \| "high"` | `"off"` | Soft glow in the fill's colour behind the filled blocks. |
| `animate` | `boolean` | `true` | Fill the blocks in one by one on mount. |
| `matrix` | `"bayer2" \| "bayer4" \| "bayer8" \| "blue-noise"` | `"bayer4"` | Threshold pattern of the gradient fill. |
| `cell` | `number` | `--dither-cell` | Size of one dither cell in CSS pixels. The ring thickness snaps to whole cells. |

## Notes [#notes]

* The gauge has `role="meter"` with `aria-valuenow`, `aria-valuemin`, `aria-valuemax` and an `aria-valuetext` that includes the unit. The drawing is hidden from assistive technology.
* Blocks fade in one after another when the gauge appears or the value rises. With reduced motion turned on they appear at once.
* It is plain SVG with one pattern per density, so it costs nothing after it renders. Bloom is a blurred copy of the filled blocks.