Target Indicator

Shows performance against a target.

New customers
82 / 100

html

<div class="target target--on-track" style="--c:#A8E6C1;">
  <div class="target__head">
    <span class="target__metric">New customers</span>
    <span class="target__status"></span>
  </div>
  <div class="target__track" role="img" aria-label="82 of 100 target">
    <div class="target__fill" style="width:82%;"></div>
    <div class="target__mark" style="left:100%;"></div>
  </div>
  <div class="target__figures">82 / 100</div>
</div>
<style>
.target { font: 13px system-ui, sans-serif; color: #241430; max-width: 280px; }
.target__head { display: flex; justify-content: space-between; margin-bottom: 6px; }
.target__status { font-size: 12px; font-weight: 600; }
.target--below .target__status::before { content: "Below target"; color: #b3261e; }
.target--on-track .target__status::before { content: "On track"; color: #2f8f4e; }
.target--exceeded .target__status::before { content: "Target exceeded"; color: var(--c); }
.target__track { position: relative; height: 10px; border-radius: 999px; background: #24143014; }
.target__fill { height: 100%; border-radius: 999px; background: var(--c); }
.target--below .target__fill { background: #b3261e; }
.target--exceeded .target__fill { background: #2f8f4e; }
.target__mark { position: absolute; top: -3px; width: 2px; height: 16px; background: #241430; }
.target__figures { margin-top: 6px; font-size: 12px; opacity: .7; }
</style>

tailwind

<div class="target-tw target-tw--on-track max-w-xs font-sans text-[13px] text-[#241430]" style="--c:#A8E6C1;">
  <div class="flex justify-between mb-1.5">
    <span>New customers</span>
    <span class="target-tw__status text-xs font-semibold"></span>
  </div>
  <div class="relative h-2.5 rounded-full bg-[#24143014]" role="img" aria-label="82 of 100 target">
    <div class="target-tw__fill h-full rounded-full" style="width:82%;background:var(--c);"></div>
    <div class="absolute -top-[3px] w-0.5 h-4 bg-[#241430]" style="left:100%;"></div>
  </div>
  <div class="mt-1.5 text-xs opacity-70">82 / 100</div>
</div>
<style>
.target-tw--below .target-tw__status::before { content: "Below target"; color: #b3261e; }
.target-tw--on-track .target-tw__status::before { content: "On track"; color: #2f8f4e; }
.target-tw--exceeded .target-tw__status::before { content: "Target exceeded"; color: var(--c); }
.target-tw--below .target-tw__fill { background: #b3261e !important; }
.target-tw--exceeded .target-tw__fill { background: #2f8f4e !important; }
</style>

react

export function TargetIndicator({
  metric = "New customers",
  actual = 82,
  target = 100,
  status = "on-track",
  color = "#A8E6C1",
}) {
  const statusText = { below: "Below target", "on-track": "On track", exceeded: "Target exceeded" }[status] ?? "On track";
  const statusColor = { below: "#b3261e", "on-track": "#2f8f4e", exceeded: color }[status] ?? "#2f8f4e";
  const fillColor = { below: "#b3261e", "on-track": color, exceeded: "#2f8f4e" }[status] ?? color;
  return (
    <div style={{ font: "13px system-ui, sans-serif", color: "#241430", maxWidth: 280 }}>
      <div style={{ display: "flex", justifyContent: "space-between", marginBottom: 6 }}>
        <span>{metric}</span>
        <span style={{ fontSize: 12, fontWeight: 600, color: statusColor }}>{statusText}</span>
      </div>
      <div role="img" aria-label={actual + " of " + target + " target"} style={{ position: "relative", height: 10, borderRadius: 999, background: "#24143014" }}>
        <div style={{ height: "100%", width: actual + "%", borderRadius: 999, background: fillColor }} />
        <div style={{ position: "absolute", top: -3, left: target + "%", width: 2, height: 16, background: "#241430" }} />
      </div>
      <div style={{ marginTop: 6, fontSize: 12, opacity: 0.7 }}>{actual} / {target}</div>
    </div>
  );
}