Skip to main content

BulletChart

Comparison

Stephen Few's bullet graph — the canonical “am I on target?” KPI micro-visual.

EMEA
AMER
APAC
LATAM
import { BulletChart } from "@elabs-ai/components-charts";
Install
npm install @elabs-ai/components-tokens @elabs-ai/components-ui @elabs-ai/components-charts

Skipping the @source line is the most common first failure: Tailwind does not scan node_modules, so every component renders unstyled. The one-time wiring

Prompt for your coding agent

Paste it into Claude Code, Cursor, Codex or any other coding agent. Text in [brackets] is yours to fill in.

Add the brand-ui BulletChart chart (from @elabs-ai/components-charts) to this project.

1. Read https://elabs-ai.com/llms.txt first: it maps every package and the rules. If you can use MCP servers, connect https://elabs-ai.com/mcp and use its search, docs and tokens tools. If not, run the same lookups in the terminal: `npx -y @elabs-ai/components-cli search "<what you need>"` and `npx -y @elabs-ai/components-cli docs <Component>`.
2. Look BulletChart up before writing code: `npx -y @elabs-ai/components-cli docs BulletChart` prints its real props, variants, what it pairs with and what to avoid.
3. If @elabs-ai/components-charts is not installed yet: `pnpm add @elabs-ai/components-tokens @elabs-ai/components-ui @elabs-ai/components-charts` (or `npm install @elabs-ai/components-tokens @elabs-ai/components-ui @elabs-ai/components-charts`). If this is the first brand-ui package in the project, wire it once: import `@elabs-ai/components-tokens/styles.css` plus the `themes/light.css` and `themes/dark.css` stylesheets in the CSS entry, add one Tailwind `@source` line per installed @elabs-ai package (Tailwind does not scan node_modules, so without it everything renders unstyled), and wrap the app root in `ThemeProvider` from `@elabs-ai/components-tokens`.
4. Use it here: [the screen or file, and what it should show]
5. Rules: use only @elabs-ai/components-* components and the semantic tokens — no raw colours, no raw font sizes, no arbitrary Tailwind values. Look up each component's real props with the docs tool before you use it; never guess a prop.
6. Done means: the typecheck passes, `npx -y @elabs-ai/components-cli audit src --strict` reports nothing, and you have opened the app and looked at every screen you touched in the light and the dark theme.

Use it for

  • How do these compare? Progress against a target.
  • a single value against a target and 2–3 qualitative bands

Avoid

  • more than one value/target pair needs comparing — use a small-multiple row of bullets or a `DumbbellChart`

Examples

8 live examples

Default

`size="md"` with bands + a target — the standard reading, axis included.

Loading

Loading skeleton (RM-183) — shown while `status="loading"`, sized like the real chart.

With Comparative

A second reference (last year) drawn as a small triangle notch, a shape distinct from the target's tick.

Over Target

The value clamps visually at the domain ceiling once it passes the target — the accessible name still states the real number.

No Bands

No `bands` prop — a single neutral track behind the bar.

Lower Is Better

`higherIsBetter={false}` — a lower-is-better measure (e.g. cost). The worst band sits at the HIGH end here, so the shade ramp mirrors: the darkest rung shades the high (worst) end instead of the low end.

Vertical

`orientation="vertical"` — the bar grows upward; the parent box supplies the height.

Small In Card

`size="sm"` (the default) — word-sized, no axis, sitting inline inside a narrow card.

API

BulletChart

PropTypeDescription
valuerequirednumberThe actual value — drawn as the bar.
targetnumberThe target — drawn as a tick, taller and darker than the bar.
comparativenumberA second reference (e.g. last year) — a small marker, a distinct shape from the target tick.
bandsBulletBand[]Qualitative ranges (ascending `to`), drawn as 2–3 neutral steps behind the bar.
minnumberScale floor. Default `0` — bars are zero-based; a caller-supplied negative floor is only honored when `value` itself is negative.
maxnumberScale ceiling. Default: the largest of `value`/`target`/`comparative`/the last band's `to`, "nice"-rounded with 5% headroom.
orientationBulletChartOrientationBar direction. Default `"horizontal"`.
sizeBulletChartSize`"sm"` (default) is word-sized with no axis; `"md"` adds a hairline tick axis.
showAxisbooleanShow the hairline tick axis. Default `size === "md"`.
valueFormatChartValueFormatHow the value/target/comparative numbers are formatted. Default `"compact"`, one notation shared across the whole scale.
messagesBulletChartLabels & ChartMessagesThis chart's own words (the `messages` group, RM-191): the names interpolated into the auto-generated accessible description (`value`, `target`, `comparative`), and overrides of the catalogue's `charts.*` keys it prints (`charts.bulletChart.*`), for this chart only. The two key sets never collide: a catalogue key always starts with `charts.`.
labelsBulletChartLabels@deprecated Use `messages` — the same object, the same keys (RM-191, ADR 0042 A.1). Still read until 7.0.0, with one development warning; when both are set, `messages` wins.
higherIsBetterbooleanWhether ASCENDING band values read better (default `true`). Bands are always drawn low→high by position (`to` is ascending), but which END is "worst" depends on the measure: a lower-is-better KPI (e.g. cost) has its worst band at the HIGH end. Flips which side of the shade ramp gets the darkest (worst) rung so the visual always reads poor→good in the metric's own good direction, never just left→right.
paletteChartPaletteColour ramp for the measure bar (RM-186): it takes the palette's first colour. Unset: `--chart-1`, as before.
accessibleLabelstringAccessible name for the chart region. AT announces this when the container receives focus or is read in flow. Example: "Monthly revenue bar chart"
accessibleDescriptionstringSupplemental description (series names, value ranges, last values, etc.). Rendered as a visually-hidden `<span>` associated via `aria-describedby`. Example: "Series: Revenue, Expenses. Range: 0–25,000."
plotHeightResponsive<ChartPlotHeight>Height of the plot: pixels, or `{ aspect }` as width divided by height. Set `{ base, medium, narrow }` to vary it by breakpoint.
marginnumber | Partial<Margin>Space around the plot, in pixels: one number for every side, or per side.
statusChartStatusShow the loading skeleton until the data is ready.
localestringBCP 47 locale the numbers are formatted in.
currencystringISO 4217 currency code for currency values.
maxFractionDigitsnumberMost digits printed after the decimal point.

Also accepts: Omit<HTMLAttributes<HTMLDivElement>, "children">, ChartA11yProps, Pick<FrameSizeGroupProps, "margin" | "plotHeight">, Pick<ChartStateGroupProps, "status">, Pick<ValueFormatGroupProps, "locale" | "currency" | "maxFractionDigits">