Skip to content
microcharts
ReferenceCoreBullet

Bullet

One measured value against a target tick, over qualitative bands, in a single row.

encodes
position (measure length vs a target tick)
precision
high
nodes
≤ 6

Quota attainment is 72 against a target of 80. Bullet puts the measure, the target tick, and the qualitative bands (poor / okay / good) on one row, for progress, SLAs, budgets, and quotas. The bands sit lowest and graduate in shade by step, because they are context and the measure bar has to read as the loudest mark on the row. The measure is a thin band centered in the track (Few's proportion, not a full-height fill), so the bands stay legible on both sides of it.

Bullet
interactive · 3.77 kB · static · 2.39 kB

Install

Import & usereact/bullet
import { Bullet } from "@microcharts/react/bullet";<Bullet value={72} target={80} bands={[50, 90]} title="Quota" />
Needs package + stylesheetSet up with AI

Try it

When to use it

Use it for progress to a goal, a value against a target, and KPIs with thresholds. For trends over time use Sparkline; it is not a distribution chart.

Sizing

width and height are viewBox units that also set the rendered pixel box — a bullet reads best wide and short. Omit them and drive the width from CSS to fill a table cell or card column; the viewBox keeps the aspect ratio.

Variants

behind target
ahead of target
no bands

Edge cases

value beyond the domain
no data

Four homes

Accessibility

Bullet announces its value against its target — "72 of 80 target.". No color is required to read whether the target was met: the measure's length and the target tick encode it by position, and the qualitative bands sit behind them. The target is a tick rather than a second bar: a distinct shape at a distinct position, so the comparison survives grayscale print and never depends on two colors staying apart. The interactive entry reads the value and target on hover and focus.

This chart is a single unit, so there is nothing to rove between: a click, tap, Enter or Space selects it and fires onSelect, and no selection stays pinned. That is the scalar half of the shared interaction contract. Hover or focus also reveals the reading itself in a floating chip, for the sizes and label modes where the mark does not print it; readout={false} drops the chip and keeps everything else.

Props

PropTypeNotes
value*numberThe measured value.
targetnumberTarget tick to compare against.
bandsnumber[]Ascending qualitative thresholds.
domain[number, number]Explicit [0, max]; auto-fit otherwise.
label"none" | "value" | "target" | "both"Value/target readout in a right gutter (default none).
titlestringAccessible name; joins the auto summary.
summarystring | falseOverride or disable the auto summary.
animatebooleaninteractiveOpt-in entrance motion when the chart mounts client-side — add import "@microcharts/react/motion" once. Inert on the server, on hydrated server HTML, and under prefers-reduced-motion.

Plus the shared grammar — data, domain, color, title, summary, format — and the layout props (width, height, className, style) that every chart accepts. Interactive entries also share animate and live, and — wherever a chart has more than one navigable unit — onActive, onSelect, selectedIndex and defaultSelectedIndex; and — wherever the chart shows a hover value — readout. See the shared grammar.

Related charts