# Bullet (/docs/charts/bullet)

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.

```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.

## Try it

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

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

## 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](/docs/charts/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.

**default**

```tsx
// data alone → an intrinsic 80×16 box
<Bullet value={72} target={80} bands={[50, 90]} />
```

**fixed size**

```tsx
// a bullet reads best wide and short
<Bullet value={72} target={80} bands={[50, 90]} width={240} height={24} />
```

**responsive**

```tsx
// fills a table cell or card column, aspect ratio preserved
<div style={{ width: "100%", maxWidth: 320 }}>
  <Bullet value={72} target={80} bands={[50, 90]} style={{ width: "100%", height: "auto" }} />
</div>
```

## 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]} />
```

## Four homes

**In a sentence**

```tsx
<p>
  Q3 quota attainment sits at{" "}
  <span className="mc-inline">
    <Bullet value={72} target={80} bands={[50, 90]} height={14} summary={false} />
  </span>{" "}
  — inside the good band, short of target.
</p>
```

**In a table cell**

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

**In a KPI card**

```tsx
<div className="kpi">
  <span className="figure">72 GB</span>
  <span className="unit">of an 80 GB volume</span>
  <Bullet value={72} target={80} bands={[50, 90]} />
</div>
```

**In a tab header**

```tsx
<button className="tab">
  North <Bullet value={72} target={80} bands={[50, 90]} />
</button>
```

## 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](/docs/accessibility#one-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

| 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).
