# Bullet (/docs/charts/bullet)

Bullet compares one measured value to a target, against a backdrop of qualitative bands (poor / okay / good). It answers
"are we there yet?" in a single row — ideal for progress, SLAs, budgets, and quotas.

```tsx
<Bullet value={72} target={80} bands={[50, 90]} width={200} height={22} title="Quota attainment" />
```

## Install

```tsx
import { Bullet } from "@microcharts/react/bullet";

<Bullet value={72} target={80} bands={[50, 90]} title="Quota" />
```

Setup (package + stylesheet): [Quickstart](/docs/quickstart#set-up-with-an-ai-agent) or paste [`/agent-setup.md`](/agent-setup.md) into your agent.


## When to use it

- **Good for** — progress to a goal, a value against a target, KPIs with thresholds.
- **Avoid for** — trends over time (use [Sparkline](/docs/charts/sparkline)) or distributions.

## 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

```tsx
<Bullet value={72} target={80} bands={[50, 90]} />
```

```tsx
<Bullet value={94} target={80} bands={[50, 90]} />
```

```tsx
<Bullet value={38} target={80} />
```

## Edge cases

```tsx
// the measure clamps at the track end; the summary keeps the real number
<Bullet value={120} target={80} domain={[0, 100]} />
```

```tsx
// a non-finite value renders a zero-width measure and announces "No data." —
// the bands and target tick stay as context
<Bullet value={NaN} target={80} bands={[50, 90]} />
```


## Why this default

The qualitative bands sit lowest and graduate in shade by step, because they are context, not the answer — the measure
bar has to read as the loudest mark on the row. The target is a tick, not a second bar: a distinct shape at a distinct
position so "value vs target" survives grayscale print and never depends on the two colors staying apart. The measure
bar itself 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.

## 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
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](/docs/accessibility#one-interaction-contract).

## Props

| Prop | Type | Description |
| --- | --- | --- |
| `value` (required) | `number` | The measured value. |
| `target` | `number` | Target tick to compare against. |
| `bands` | `number[]` | 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). |
| `title` | `string` | Accessible name; joins the auto summary. |
| `summary` | `string \| false` | Override or disable the auto summary. |
| `animate` | `boolean` | (interactive) Opt-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](/docs/quickstart#the-shared-grammar).
