Basic Bullet Chart — MUI X Charts

A bullet chart displays a single measure against qualitative ranges and a target marker, designed by Stephen Few as a space-efficient alternative to gauge charts. The linear format shows actual performance as a bar, a target as a vertical marker, and background bands representing qualitative ranges (poor/satisfactory/good). Its compact design allows multiple bullet charts to fit on a single dashboard for easy comparison across metrics.

Basic Bullet Chart rendered with MUI X Charts

Renders

JavaScript source (MUI X Charts)

// anyplot.ai
// bullet-basic: Basic Bullet Chart
// Library: muix 7.29.1 | JavaScript 22.23.2
// Quality: 91/100 | Created: 2026-08-24
import { BarChart } from "@mui/x-charts/BarChart";
import { ChartsReferenceLine } from "@mui/x-charts/ChartsReferenceLine";

const t = window.ANYPLOT_TOKENS;
const SIZE = window.ANYPLOT_SIZE;

// --- Data (in-memory, deterministic) ----------------------------------------
// Quarterly KPI dashboard: actual performance vs. target, banded by
// poor / satisfactory / good qualitative ranges (Stephen Few's design).
const kpis = [
  { label: "Quarterly Revenue ($M)", actual: 82, target: 90, ranges: [50, 75, 100] },
  { label: "New Customers Acquired", actual: 145, target: 130, ranges: [80, 120, 160] },
  { label: "Support SLA Compliance (%)", actual: 96, target: 95, ranges: [85, 95, 100] },
  { label: "Project Milestones (%)", actual: 68, target: 80, ranges: [40, 70, 100] },
];

const hexToRgba = (hex, alpha) => {
  const int = parseInt(hex.slice(1), 16);
  const r = (int >> 16) & 255;
  const g = (int >> 8) & 255;
  const b = int & 255;
  return `rgba(${r}, ${g}, ${b}, ${alpha})`;
};

// Grayscale range-band shading (darkest = poor, lightest = good) per spec notes.
const BAND_COLORS = [hexToRgba(t.ink, 0.26), hexToRgba(t.ink, 0.15), hexToRgba(t.ink, 0.08)];
const BAR_COLOR = t.palette[0]; // brand green — the measure, identical across every bullet

// --- Layout constants (CSS px within the mount's coordinate space) ---------
// BAND_HEIGHT occupies most of each row (denser, space-efficient dashboard
// block per the spec's own framing) instead of a thin band lost in tall rows.
const PADDING = 40;
const TITLE_HEIGHT = 40;
const KEY_HEIGHT = 24;
const ROW_GAP = 14;
const LABEL_WIDTH = 300;
const VALUE_WIDTH = 150;
const BAND_HEIGHT = 110;

const rowAreaHeight =
  SIZE.height - PADDING * 2 - TITLE_HEIGHT - KEY_HEIGHT - ROW_GAP * (kpis.length + 1);
const rowHeight = rowAreaHeight / kpis.length;
const chartWidth = SIZE.width - PADDING * 2 - LABEL_WIDTH - VALUE_WIDTH;

function Bullet({ kpi }) {
  const maxRange = kpi.ranges[2];
  const bands = [kpi.ranges[0], kpi.ranges[1] - kpi.ranges[0], kpi.ranges[2] - kpi.ranges[1]];

  return (
    <div style={{ display: "flex", alignItems: "center", height: rowHeight, gap: 20 }}>
      <div
        style={{
          width: LABEL_WIDTH,
          textAlign: "right",
          color: t.ink,
          fontSize: 16,
          fontWeight: 500,
          paddingRight: 8,
        }}
      >
        {kpi.label}
      </div>
      <div style={{ position: "relative", width: chartWidth, height: BAND_HEIGHT }}>
        {/* Background: stacked qualitative range bands, grayscale, full band height */}
        <BarChart
          width={chartWidth}
          height={BAND_HEIGHT}
          layout="horizontal"
          series={bands.map((value, i) => ({ data: [value], stack: "range", color: BAND_COLORS[i] }))}
          xAxis={[{ scaleType: "linear", min: 0, max: maxRange }]}
          yAxis={[{ scaleType: "band", data: [""], categoryGapRatio: 0.08 }]}
          margin={{ top: 0, bottom: 0, left: 0, right: 0 }}
          bottomAxis={null}
          leftAxis={null}
          slotProps={{ legend: { hidden: true } }}
          tooltip={{ trigger: "none" }}
          skipAnimation
        />
        {/* Foreground: thin actual-value bar plus the target reference line */}
        <div style={{ position: "absolute", top: 0, left: 0 }}>
          <BarChart
            width={chartWidth}
            height={BAND_HEIGHT}
            layout="horizontal"
            series={[{ data: [kpi.actual], color: BAR_COLOR }]}
            xAxis={[{ scaleType: "linear", min: 0, max: maxRange }]}
            yAxis={[{ scaleType: "band", data: [""], categoryGapRatio: 0.6 }]}
            margin={{ top: 0, bottom: 0, left: 0, right: 0 }}
            bottomAxis={null}
            leftAxis={null}
            slotProps={{ legend: { hidden: true } }}
            tooltip={{ trigger: "none" }}
            skipAnimation
          >
            <ChartsReferenceLine
              x={kpi.target}
              lineStyle={{ stroke: t.ink, strokeWidth: 3 }}
              label={`${kpi.target}`}
              labelStyle={{ fill: t.ink, fontSize: 11, fontWeight: 600 }}
              labelAlign="end"
            />
          </BarChart>
        </div>
      </div>
      <div style={{ width: VALUE_WIDTH, color: t.inkSoft, fontSize: 15 }}>
        {kpi.actual} / {kpi.target}
      </div>
    </div>
  );
}

// --- Chart (default-exported component — the harness mounts it) ------------
export default function Chart() {
  return (
    <div
      style={{
        width: SIZE.width,
        height: SIZE.height,
        padding: PADDING,
        boxSizing: "border-box",
        display: "flex",
        flexDirection: "column",
      }}
    >
      <div style={{ height: TITLE_HEIGHT, color: t.ink, fontSize: 22, fontWeight: 500 }}>
        bullet-basic · javascript · muix · anyplot.ai
      </div>
      <div
        style={{
          display: "flex",
          alignItems: "center",
          height: KEY_HEIGHT,
          marginTop: ROW_GAP,
          marginLeft: LABEL_WIDTH + 20,
          gap: 20,
          fontSize: 13,
          color: t.inkSoft,
        }}
      >
        {["Poor", "Satisfactory", "Good"].map((name, i) => (
          <div key={name} style={{ display: "flex", alignItems: "center", gap: 6 }}>
            <span style={{ width: 12, height: 12, background: BAND_COLORS[i], borderRadius: 2 }} />
            {name}
          </div>
        ))}
        <div style={{ display: "flex", alignItems: "center", gap: 6 }}>
          <span style={{ width: 2, height: 12, background: t.ink }} />
          Target
        </div>
      </div>
      <div style={{ display: "flex", flexDirection: "column", gap: ROW_GAP, marginTop: ROW_GAP }}>
        {kpis.map((kpi) => (
          <Bullet key={kpi.label} kpi={kpi} />
        ))}
      </div>
    </div>
  );
}

Part of Basic Bullet Chart on anyplot.ai.

Other implementations