Skip to content

Bullet

A compact measure-vs-target readout: a value bar over qualitative range bands with a target marker.

Stablev0.4.2added in v0.2.0@garn/ui/bullet
On this page

Default

A value bar over three range bands with a target tick.

Kpis

A labelled stack of bullets — a compact KPI list.

Installation

garn is copy-in — the garn CLI writes this component's source into your project, so you own and can edit it. It resolves any base files and installs the npm dependencies below for you.

terminal
npx garn-ui add bullet
First time? Set up garn in your project
Registry
bullet
Deps
None
Registry deps
utils

Import

import { Bullet } from "@garn/ui/bullet";

Props

The component's public props and their types.

color
Description
Measure-bar colour (token ref).
Type
string
Default
"var(--garn-chart-1)"
max
Description
Scale max (default: the largest of value / target / ranges).
Type
number
ranges
Description
Qualitative range thresholds → muted background bands (e.g. [50, 75, 100]).
Type
number[]
target
Description
A comparison marker (target / prior period).
Type
number
valueRequired
Description
The measure (the actual value).
Type
number
valueLabel
Description
Accessible reading for the value (e.g. "$78k").
Type
string

Plus 278 inherited native <div> attributes.

Styling

Target these data-slots and states, and remap these tokens, to restyle without forking the component.

[data-slot="bullet"][data-slot="bullet-ranges"][data-slot="bullet-measure"][data-slot="bullet-target"]

When to use

Show one measure against a target (and optional qualitative bands) compactly — KPI tiles, tables, dashboards.

Reach for something else when

  • A single large standalone dial (use Gauge)
  • a ranked breakdown (use BarList)
  • a trend (use Sparkline).

Overview

Bullet is Stephen Few's answer to the gauge: a single value read against a target and a few qualitative bands (poor/ok/good), in a fraction of a gauge's space. It's ideal in dense contexts — a table cell, a KPI row — where you need measure-vs-target at a glance. It's a meter for assistive tech, so the value and target are announced, not just drawn.

Guidelines

Prefer it to a gauge when space is tight. A row of bullets compares many KPIs far better than a wall of dials. Reach for Gauge only when a single, large, standalone dial is the point.

Give the bands meaning. The qualitative ranges should map to real thresholds (e.g. target zones); keep them muted so the measure bar and target tick stand out. An explicit max fixes the scale (the value clamps to it); omit it to auto-fit.

Announce value + target. Pass an aria-label; the component folds the value and target into aria-valuetext so screen-reader users get the comparison, not just a number.

Best practices

Do
  • Pass an aria-label; set a target and meaningful ranges.
  • Use an explicit max when the scale is fixed (e.g. 0–100).
Don't
  • Don't stuff many bands in — 2–3 qualitative zones read best.
  • Don't use it where a full trend or breakdown is needed.

Troubleshooting

The value bar exceeds the track / never maxes out.

Cause. No explicit max, so the scale auto-grew to fit the value.

Fix. Pass max to fix the scale; the value then clamps to it.

Accessibility

Role
meter
A1.3.1Info and RelationshipsA4.1.2Name, Role, Value
  • role=meter with aria-valuenow (clamped) / aria-valuemin / aria-valuemax; pass an `aria-label`.
  • aria-valuetext folds in the value label and the target (e.g. "78%, target 90").
  • The bands, bar, and target tick are decorative.