Dashboard Stat Widgets
Original · freeassorted dashboard stat widgets
Copy prompt gives Claude Code, Cursor or v0 a ready-to-paste prompt that recreates this exact component.
npx shadcn@latest add https://webinnoventix.com/r/dash-stat-widgets.json"use client";
import {
useCallback,
useId,
useMemo,
useRef,
useState,
type KeyboardEvent,
type ReactNode,
} from "react";
import { motion, useReducedMotion } from "motion/react";
/* ------------------------------------------------------------------ */
/* Deterministic sample data (stable across server + client renders) */
/* ------------------------------------------------------------------ */
const DAY_MS = 86_400_000;
const ANCHOR_MS = Date.UTC(2026, 6, 17);
const HISTORY = 180;
const MONTHS = [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
] as const;
function rng(seed: number): () => number {
let t = seed >>> 0;
return () => {
t += 0x6d2b79f5;
let x = Math.imul(t ^ (t >>> 15), 1 | t);
x ^= x + Math.imul(x ^ (x >>> 7), 61 | x);
return ((x ^ (x >>> 14)) >>> 0) / 4294967296;
};
}
function dayAt(index: number): Date {
return new Date(ANCHOR_MS - (HISTORY - 1 - index) * DAY_MS);
}
const LABELS: string[] = Array.from({ length: HISTORY }, (_, i) => {
const d = dayAt(i);
return `${MONTHS[d.getUTCMonth()]} ${d.getUTCDate()}`;
});
interface SeriesSpec {
seed: number;
base: number;
drift: number;
noise: number;
weekend: number;
floor: number;
}
function makeSeries(spec: SeriesSpec): number[] {
const rand = rng(spec.seed);
const out: number[] = [];
for (let i = 0; i < HISTORY; i += 1) {
const dow = dayAt(i).getUTCDay();
const weekend = dow === 0 || dow === 6 ? spec.weekend : 1;
const raw =
(spec.base + spec.drift * i + (rand() - 0.5) * 2 * spec.noise) * weekend;
out.push(Math.max(spec.floor, raw));
}
return out;
}
const REVENUE = makeSeries({
seed: 20_260_717,
base: 26_800,
drift: 118,
noise: 1_450,
weekend: 0.78,
floor: 8_000,
});
const LATENCY = makeSeries({
seed: 44_107,
base: 331,
drift: -0.21,
noise: 24,
weekend: 0.94,
floor: 120,
});
const ERRORS = makeSeries({
seed: 90_211,
base: 0.61,
drift: -0.0011,
noise: 0.11,
weekend: 0.8,
floor: 0.03,
});
const DEPLOYS = makeSeries({
seed: 31_337,
base: 3.4,
drift: 0.004,
noise: 2.6,
weekend: 0.28,
floor: 0,
}).map((v) => Math.round(v));
const RANGES = [
{ key: "7D", days: 7, label: "Last 7 days" },
{ key: "30D", days: 30, label: "Last 30 days" },
{ key: "90D", days: 90, label: "Last 90 days" },
] as const;
type RangeKey = (typeof RANGES)[number]["key"];
const usd = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
maximumFractionDigits: 0,
});
const nf = new Intl.NumberFormat("en-US");
function sum(values: number[]): number {
return values.reduce((a, b) => a + b, 0);
}
function avg(values: number[]): number {
return values.length === 0 ? 0 : sum(values) / values.length;
}
function pctChange(current: number, prior: number): number {
if (prior === 0) return 0;
return ((current - prior) / prior) * 100;
}
/* ------------------------------------------------------------------ */
/* Static widget content */
/* ------------------------------------------------------------------ */
interface Channel {
id: string;
name: string;
share: number;
sessions: number;
bar: string;
dot: string;
}
const CHANNELS: Channel[] = [
{
id: "organic",
name: "Organic search",
share: 41,
sessions: 184_920,
bar: "bg-indigo-500 dark:bg-indigo-400",
dot: "bg-indigo-500 dark:bg-indigo-400",
},
{
id: "direct",
name: "Direct",
share: 23,
sessions: 103_740,
bar: "bg-violet-500 dark:bg-violet-400",
dot: "bg-violet-500 dark:bg-violet-400",
},
{
id: "referral",
name: "Referral",
share: 16,
sessions: 72_160,
bar: "bg-sky-500 dark:bg-sky-400",
dot: "bg-sky-500 dark:bg-sky-400",
},
{
id: "paid",
name: "Paid social",
share: 12,
sessions: 54_120,
bar: "bg-emerald-500 dark:bg-emerald-400",
dot: "bg-emerald-500 dark:bg-emerald-400",
},
{
id: "email",
name: "Lifecycle email",
share: 8,
sessions: 36_080,
bar: "bg-amber-500 dark:bg-amber-400",
dot: "bg-amber-500 dark:bg-amber-400",
},
];
const TOTAL_SESSIONS = sum(CHANNELS.map((c) => c.sessions));
interface Goal {
id: string;
name: string;
detail: string;
progress: number;
bar: string;
}
const GOALS: Goal[] = [
{
id: "arr",
name: "Enterprise ARR",
detail: "$4.21M of $5.40M target",
progress: 78,
bar: "bg-indigo-500 dark:bg-indigo-400",
},
{
id: "csat",
name: "Support CSAT",
detail: "4.61 of 4.80 target",
progress: 96,
bar: "bg-emerald-500 dark:bg-emerald-400",
},
{
id: "onboard",
name: "Median onboarding",
detail: "9.1 days against a 7.0 day target",
progress: 77,
bar: "bg-amber-500 dark:bg-amber-400",
},
];
const BUDGET_USED = 63.4;
const HEAT_DAYS = 28;
const HEAT_COLS = 7;
function heatClass(count: number): string {
if (count <= 0) return "bg-zinc-100 dark:bg-zinc-800";
if (count <= 2) return "bg-emerald-200 dark:bg-emerald-900";
if (count <= 4) return "bg-emerald-400 dark:bg-emerald-700";
return "bg-emerald-600 dark:bg-emerald-400";
}
/* ------------------------------------------------------------------ */
/* Small presentational pieces */
/* ------------------------------------------------------------------ */
function TrendIcon({ up }: { up: boolean }) {
return (
<svg
viewBox="0 0 12 12"
className={`h-3 w-3 ${up ? "" : "rotate-180"}`}
aria-hidden="true"
fill="none"
stroke="currentColor"
strokeWidth="1.6"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M6 9.5V2.5" />
<path d="M2.75 5.75 6 2.5l3.25 3.25" />
</svg>
);
}
function Delta({ value, good }: { value: number; good: boolean }) {
const up = value >= 0;
return (
<span
className={`inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-semibold tabular-nums ${
good
? "bg-emerald-50 text-emerald-700 dark:bg-emerald-500/10 dark:text-emerald-300"
: "bg-rose-50 text-rose-700 dark:bg-rose-500/10 dark:text-rose-300"
}`}
>
<TrendIcon up={up} />
<span className="sr-only">{up ? "Up" : "Down"} </span>
{Math.abs(value).toFixed(1)}%
</span>
);
}
function Card({
className = "",
children,
}: {
className?: string;
children: ReactNode;
}) {
return (
<div
className={`rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-800 dark:bg-zinc-900/60 ${className}`}
>
{children}
</div>
);
}
/* ------------------------------------------------------------------ */
/* Component */
/* ------------------------------------------------------------------ */
export default function DashStatWidgets() {
const reduced = useReducedMotion();
const uid = useId();
const areaId = `${uid}-area`;
const gaugeId = `${uid}-gauge`;
const [range, setRange] = useState<RangeKey>("30D");
const [compare, setCompare] = useState<boolean>(true);
const [scrub, setScrub] = useState<number | null>(null);
const [hidden, setHidden] = useState<string[]>([]);
const [heatSel, setHeatSel] = useState<number>(HEAT_DAYS - 1);
const [heatFocus, setHeatFocus] = useState<number>(HEAT_DAYS - 1);
const rangeRefs = useRef<Array<HTMLButtonElement | null>>([]);
const heatRefs = useRef<Array<HTMLButtonElement | null>>([]);
const days = RANGES.find((r) => r.key === range)?.days ?? 30;
const win = useMemo(() => {
const slice = (arr: number[]) => ({
now: arr.slice(HISTORY - days),
prior: arr.slice(HISTORY - days * 2, HISTORY - days),
});
return {
revenue: slice(REVENUE),
latency: slice(LATENCY),
errors: slice(ERRORS),
deploys: slice(DEPLOYS),
labels: LABELS.slice(HISTORY - days),
};
}, [days]);
const revTotal = sum(win.revenue.now);
const revPrior = sum(win.revenue.prior);
const revDelta = pctChange(revTotal, revPrior);
const chart = useMemo(() => {
const pool = compare
? [...win.revenue.now, ...win.revenue.prior]
: win.revenue.now;
const rawLo = Math.min(...pool);
const rawHi = Math.max(...pool);
const pad = (rawHi - rawLo) * 0.14 || 1;
const lo = rawLo - pad;
const hi = rawHi + pad;
const norm = (v: number) => (v - lo) / (hi - lo);
const path = (values: number[]): string =>
values
.map((v, i) => {
const x = (i / (values.length - 1)) * 100;
const y = (1 - norm(v)) * 100;
return `${i === 0 ? "M" : "L"}${x.toFixed(3)},${y.toFixed(3)}`;
})
.join(" ");
return {
line: path(win.revenue.now),
area: `${path(win.revenue.now)} L100,100 L0,100 Z`,
prior: path(win.revenue.prior),
norm,
};
}, [compare, win.revenue]);
const activeIndex = scrub ?? win.revenue.now.length - 1;
const activeValue = win.revenue.now[activeIndex] ?? 0;
const peak = Math.max(...win.revenue.now);
const low = Math.min(...win.revenue.now);
const tiles = useMemo(() => {
const latencyNow = avg(win.latency.now);
const latencyWas = avg(win.latency.prior);
const errorNow = avg(win.errors.now);
const errorWas = avg(win.errors.prior);
const deployNow = sum(win.deploys.now);
const deployWas = sum(win.deploys.prior);
return [
{
id: "latency",
label: "p95 latency",
value: `${Math.round(latencyNow)} ms`,
delta: pctChange(latencyNow, latencyWas),
goodWhenDown: true,
spark: win.latency.now.slice(-14),
note: `Prior period ${Math.round(latencyWas)} ms`,
},
{
id: "errors",
label: "Error rate",
value: `${errorNow.toFixed(2)}%`,
delta: pctChange(errorNow, errorWas),
goodWhenDown: true,
spark: win.errors.now.slice(-14),
note: `Prior period ${errorWas.toFixed(2)}%`,
},
{
id: "deploys",
label: "Deploys shipped",
value: `${deployNow}`,
delta: pctChange(deployNow, deployWas),
goodWhenDown: false,
spark: win.deploys.now.slice(-14),
note: `Prior period ${deployWas} deploys`,
},
];
}, [win]);
const visible = CHANNELS.filter((c) => !hidden.includes(c.id));
const visibleTotal = sum(visible.map((c) => c.share));
const visibleSessions = sum(visible.map((c) => c.sessions));
const heat = useMemo(() => {
const counts = DEPLOYS.slice(HISTORY - HEAT_DAYS);
const labels = LABELS.slice(HISTORY - HEAT_DAYS);
return counts.map((count, i) => ({ count, label: labels[i] }));
}, []);
const onRangeKeys = useCallback(
(event: KeyboardEvent<HTMLDivElement>) => {
const index = RANGES.findIndex((r) => r.key === range);
let next = index;
if (event.key === "ArrowRight" || event.key === "ArrowDown") {
next = (index + 1) % RANGES.length;
} else if (event.key === "ArrowLeft" || event.key === "ArrowUp") {
next = (index - 1 + RANGES.length) % RANGES.length;
} else if (event.key === "Home") {
next = 0;
} else if (event.key === "End") {
next = RANGES.length - 1;
} else {
return;
}
event.preventDefault();
setRange(RANGES[next].key);
setScrub(null);
rangeRefs.current[next]?.focus();
},
[range],
);
const onHeatKeys = useCallback(
(event: KeyboardEvent<HTMLButtonElement>, index: number) => {
let next = index;
if (event.key === "ArrowRight") next = Math.min(HEAT_DAYS - 1, index + 1);
else if (event.key === "ArrowLeft") next = Math.max(0, index - 1);
else if (event.key === "ArrowDown")
next = Math.min(HEAT_DAYS - 1, index + HEAT_COLS);
else if (event.key === "ArrowUp") next = Math.max(0, index - HEAT_COLS);
else if (event.key === "Home") next = 0;
else if (event.key === "End") next = HEAT_DAYS - 1;
else return;
event.preventDefault();
setHeatFocus(next);
heatRefs.current[next]?.focus();
},
[],
);
const toggleChannel = useCallback((id: string) => {
setHidden((prev) => {
if (prev.includes(id)) return prev.filter((x) => x !== id);
if (prev.length >= CHANNELS.length - 1) return prev;
return [...prev, id];
});
}, []);
const gaugeR = 54;
const gaugeC = 2 * Math.PI * gaugeR;
const gaugeArc = gaugeC * 0.75;
const gaugeTarget = gaugeArc * (1 - BUDGET_USED / 100);
return (
<section className="relative w-full bg-zinc-50 px-4 py-20 sm:px-6 sm:py-28 lg:px-8 dark:bg-zinc-950">
<style>{`
@keyframes dsw-pulse {
0% { transform: scale(1); opacity: 0.65; }
70% { transform: scale(2.4); opacity: 0; }
100% { transform: scale(2.4); opacity: 0; }
}
.dsw-pulse-ring { animation: dsw-pulse 2.4s cubic-bezier(0.4, 0, 0.2, 1) infinite; }
@keyframes dsw-sheen {
0% { transform: translateX(-100%); }
55% { transform: translateX(320%); }
100% { transform: translateX(320%); }
}
.dsw-sheen { animation: dsw-sheen 3.6s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) {
.dsw-pulse-ring, .dsw-sheen { animation: none !important; }
}
`}</style>
<div className="mx-auto max-w-6xl">
{/* Header */}
<div className="flex flex-col gap-6 sm:flex-row sm:items-end sm:justify-between">
<div>
<p className="flex items-center gap-2 text-xs font-semibold uppercase tracking-[0.18em] text-indigo-600 dark:text-indigo-400">
<span className="relative inline-flex h-2 w-2">
<span className="dsw-pulse-ring absolute inline-flex h-full w-full rounded-full bg-emerald-500" />
<span className="relative inline-flex h-2 w-2 rounded-full bg-emerald-500" />
</span>
Reliability & growth
</p>
<h2 className="mt-3 text-3xl font-semibold tracking-tight text-zinc-900 sm:text-4xl dark:text-zinc-50">
Platform overview
</h2>
<p className="mt-3 max-w-xl text-sm leading-relaxed text-zinc-600 dark:text-zinc-400">
Every widget reads from one deterministic series. Change the range
or scrub the chart and the totals, deltas and micro-charts all
recompute together.
</p>
</div>
<div className="flex flex-wrap items-center gap-3">
<div
role="radiogroup"
aria-label="Reporting range"
onKeyDown={onRangeKeys}
className="inline-flex rounded-xl border border-zinc-200 bg-white p-1 shadow-sm dark:border-zinc-800 dark:bg-zinc-900"
>
{RANGES.map((r, i) => {
const active = r.key === range;
return (
<button
key={r.key}
ref={(el) => {
rangeRefs.current[i] = el;
}}
type="button"
role="radio"
aria-checked={active}
aria-label={r.label}
tabIndex={active ? 0 : -1}
onClick={() => {
setRange(r.key);
setScrub(null);
}}
className={`rounded-lg px-3 py-1.5 text-sm font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-zinc-900 ${
active
? "bg-zinc-900 text-white dark:bg-zinc-100 dark:text-zinc-900"
: "text-zinc-600 hover:bg-zinc-100 dark:text-zinc-400 dark:hover:bg-zinc-800"
}`}
>
{r.key}
</button>
);
})}
</div>
<button
type="button"
role="switch"
aria-checked={compare}
onClick={() => setCompare((v) => !v)}
className="group inline-flex items-center gap-2.5 rounded-xl border border-zinc-200 bg-white px-3 py-2 text-sm font-medium text-zinc-700 shadow-sm transition-colors hover:bg-zinc-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-zinc-50 dark:border-zinc-800 dark:bg-zinc-900 dark:text-zinc-300 dark:hover:bg-zinc-800 dark:focus-visible:ring-offset-zinc-950"
>
<span
aria-hidden="true"
className={`relative h-5 w-9 shrink-0 rounded-full transition-colors ${
compare
? "bg-indigo-600 dark:bg-indigo-500"
: "bg-zinc-300 dark:bg-zinc-700"
}`}
>
<span
className={`absolute top-0.5 h-4 w-4 rounded-full bg-white shadow transition-all ${
compare ? "left-[1.125rem]" : "left-0.5"
}`}
/>
</span>
Compare prior period
</button>
</div>
</div>
{/* Grid */}
<div className="mt-10 grid grid-cols-1 gap-4 lg:grid-cols-6">
{/* Revenue KPI + sparkline */}
<Card className="lg:col-span-4">
<div className="flex flex-wrap items-start justify-between gap-4">
<div>
<h3 className="text-sm font-medium text-zinc-600 dark:text-zinc-400">
Net revenue
</h3>
<p className="mt-1.5 text-3xl font-semibold tabular-nums tracking-tight text-zinc-900 dark:text-zinc-50">
{usd.format(revTotal)}
</p>
<p className="mt-1 text-xs text-zinc-500 dark:text-zinc-500">
{RANGES.find((r) => r.key === range)?.label} · prior period{" "}
{usd.format(revPrior)}
</p>
</div>
<Delta value={revDelta} good={revDelta >= 0} />
</div>
<div className="relative mt-6">
<div className="relative h-44 w-full rounded-xl border border-zinc-200 bg-zinc-50/60 sm:h-52 dark:border-zinc-800 dark:bg-zinc-950/40">
<div className="absolute inset-0 overflow-hidden rounded-xl">
{[0, 25, 50, 75, 100].map((y) => (
<div
key={y}
aria-hidden="true"
className="absolute inset-x-0 border-t border-dashed border-zinc-200/80 dark:border-zinc-800/80"
style={{ top: `${y}%` }}
/>
))}
<svg
viewBox="0 0 100 100"
preserveAspectRatio="none"
className="absolute inset-0 h-full w-full"
aria-hidden="true"
focusable="false"
>
<defs>
<linearGradient id={areaId} x1="0" y1="0" x2="0" y2="1">
<stop
offset="0%"
stopColor="rgb(99 102 241)"
stopOpacity="0.34"
/>
<stop
offset="100%"
stopColor="rgb(99 102 241)"
stopOpacity="0"
/>
</linearGradient>
</defs>
{compare ? (
<path
d={chart.prior}
fill="none"
stroke="currentColor"
className="text-zinc-400 dark:text-zinc-600"
strokeWidth="1.5"
strokeDasharray="4 4"
vectorEffect="non-scaling-stroke"
/>
) : null}
<path d={chart.area} fill={`url(#${areaId})`} />
<motion.path
d={chart.line}
fill="none"
stroke="currentColor"
className="text-indigo-600 dark:text-indigo-400"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
vectorEffect="non-scaling-stroke"
initial={reduced ? false : { pathLength: 0 }}
animate={{ pathLength: 1 }}
transition={{
duration: reduced ? 0 : 0.9,
ease: "easeInOut",
}}
/>
</svg>
</div>
{/* Scrub cursor */}
<div
aria-hidden="true"
className="pointer-events-none absolute inset-y-0 w-px bg-indigo-500/70 dark:bg-indigo-400/70"
style={{
left: `${(activeIndex / Math.max(1, win.revenue.now.length - 1)) * 100}%`,
}}
/>
<div
aria-hidden="true"
className="pointer-events-none absolute h-2.5 w-2.5 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-white bg-indigo-600 shadow dark:border-zinc-900 dark:bg-indigo-400"
style={{
left: `${(activeIndex / Math.max(1, win.revenue.now.length - 1)) * 100}%`,
top: `${(1 - chart.norm(activeValue)) * 100}%`,
}}
/>
</div>
<input
type="range"
min={0}
max={win.revenue.now.length - 1}
step={1}
value={activeIndex}
onChange={(e) => setScrub(Number(e.target.value))}
aria-label="Scrub daily net revenue"
aria-valuetext={`${win.labels[activeIndex]}: ${usd.format(activeValue)}`}
className="peer absolute inset-0 h-full w-full cursor-crosshair appearance-none bg-transparent opacity-0 focus:outline-none"
/>
<div className="pointer-events-none absolute inset-0 rounded-xl ring-indigo-500 ring-offset-2 ring-offset-white peer-focus-visible:ring-2 dark:ring-offset-zinc-900" />
</div>
<div className="mt-4 flex flex-wrap items-center justify-between gap-3">
<p className="text-sm text-zinc-600 dark:text-zinc-400">
<span className="font-semibold text-zinc-900 dark:text-zinc-100">
{win.labels[activeIndex]}
</span>{" "}
· {usd.format(activeValue)}
</p>
<div className="flex flex-wrap gap-2 text-xs tabular-nums">
<span className="rounded-md bg-zinc-100 px-2 py-1 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400">
Peak {usd.format(peak)}
</span>
<span className="rounded-md bg-zinc-100 px-2 py-1 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400">
Low {usd.format(low)}
</span>
<span className="rounded-md bg-zinc-100 px-2 py-1 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400">
Avg/day {usd.format(avg(win.revenue.now))}
</span>
</div>
</div>
</Card>
{/* Error budget gauge */}
<Card className="flex flex-col lg:col-span-2">
<h3 className="text-sm font-medium text-zinc-600 dark:text-zinc-400">
Error budget consumed
</h3>
<p className="mt-1 text-xs text-zinc-500 dark:text-zinc-500">
Quarter to date · 99.9% SLO
</p>
<div className="relative mx-auto mt-4 h-40 w-40">
<svg viewBox="0 0 140 140" className="h-full w-full">
<title>{`Error budget consumed: ${BUDGET_USED}% of quarter`}</title>
<defs>
<linearGradient id={gaugeId} x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stopColor="rgb(129 140 248)" />
<stop offset="55%" stopColor="rgb(139 92 246)" />
<stop offset="100%" stopColor="rgb(244 63 94)" />
</linearGradient>
</defs>
<circle
cx="70"
cy="70"
r={gaugeR}
fill="none"
stroke="currentColor"
className="text-zinc-200 dark:text-zinc-800"
strokeWidth="12"
strokeLinecap="round"
strokeDasharray={`${gaugeArc} ${gaugeC}`}
transform="rotate(-225 70 70)"
/>
<motion.circle
cx="70"
cy="70"
r={gaugeR}
fill="none"
stroke={`url(#${gaugeId})`}
strokeWidth="12"
strokeLinecap="round"
strokeDasharray={`${gaugeArc} ${gaugeC}`}
transform="rotate(-225 70 70)"
initial={{
strokeDashoffset: reduced ? gaugeTarget : gaugeArc,
}}
animate={{ strokeDashoffset: gaugeTarget }}
transition={{
duration: reduced ? 0 : 1.1,
ease: "easeOut",
}}
/>
</svg>
<div className="pointer-events-none absolute inset-0 flex flex-col items-center justify-center">
<span className="text-3xl font-semibold tabular-nums tracking-tight text-zinc-900 dark:text-zinc-50">
{BUDGET_USED}%
</span>
<span className="mt-0.5 text-xs text-zinc-500 dark:text-zinc-500">
of budget
</span>
</div>
</div>
<dl className="mt-auto grid grid-cols-2 gap-3 border-t border-zinc-200 pt-4 text-sm dark:border-zinc-800">
<div>
<dt className="text-xs text-zinc-500 dark:text-zinc-500">
Remaining
</dt>
<dd className="font-semibold tabular-nums text-zinc-900 dark:text-zinc-100">
{(100 - BUDGET_USED).toFixed(1)}%
</dd>
</div>
<div>
<dt className="text-xs text-zinc-500 dark:text-zinc-500">
Resets
</dt>
<dd className="font-semibold text-zinc-900 dark:text-zinc-100">
Oct 1
</dd>
</div>
</dl>
</Card>
{/* Micro stat tiles */}
{tiles.map((tile) => {
const good = tile.goodWhenDown ? tile.delta < 0 : tile.delta >= 0;
const sparkHi = Math.max(...tile.spark);
const sparkLo = Math.min(...tile.spark);
return (
<Card key={tile.id} className="lg:col-span-2">
<div className="flex items-start justify-between gap-3">
<h3 className="text-sm font-medium text-zinc-600 dark:text-zinc-400">
{tile.label}
</h3>
<Delta value={tile.delta} good={good} />
</div>
<p className="mt-2 text-2xl font-semibold tabular-nums tracking-tight text-zinc-900 dark:text-zinc-50">
{tile.value}
</p>
<div
className="mt-4 flex h-10 items-end gap-1"
aria-hidden="true"
>
{tile.spark.map((v, i) => {
const h =
sparkHi === sparkLo
? 55
: 18 + ((v - sparkLo) / (sparkHi - sparkLo)) * 82;
return (
<motion.span
key={`${tile.id}-${i}`}
className={`flex-1 rounded-sm ${
i === tile.spark.length - 1
? "bg-indigo-500 dark:bg-indigo-400"
: "bg-zinc-200 dark:bg-zinc-700"
}`}
initial={reduced ? false : { scaleY: 0 }}
animate={{ scaleY: 1 }}
style={{ height: `${h}%`, transformOrigin: "bottom" }}
transition={{
duration: reduced ? 0 : 0.4,
delay: reduced ? 0 : i * 0.02,
ease: "easeOut",
}}
/>
);
})}
</div>
<p className="mt-3 text-xs text-zinc-500 dark:text-zinc-500">
{tile.note}
</p>
</Card>
);
})}
{/* Channel mix */}
<Card className="flex flex-col lg:col-span-3">
<div className="flex items-start justify-between gap-3">
<div>
<h3 className="text-sm font-medium text-zinc-600 dark:text-zinc-400">
Acquisition mix
</h3>
<p className="mt-1 text-xs text-zinc-500 dark:text-zinc-500">
{nf.format(TOTAL_SESSIONS)} sessions · toggle a channel to
re-weight the split
</p>
</div>
</div>
<div className="mt-5 flex h-3 w-full gap-0.5 overflow-hidden rounded-full">
{visible.map((c) => (
<motion.div
key={c.id}
className={`h-full first:rounded-l-full last:rounded-r-full ${c.bar}`}
initial={false}
animate={{ width: `${(c.share / visibleTotal) * 100}%` }}
transition={{ duration: reduced ? 0 : 0.45, ease: "easeOut" }}
/>
))}
</div>
<ul className="mt-5 space-y-1">
{CHANNELS.map((c) => {
const off = hidden.includes(c.id);
const share = off ? 0 : (c.share / visibleTotal) * 100;
return (
<li key={c.id}>
<button
type="button"
aria-pressed={!off}
onClick={() => toggleChannel(c.id)}
className="flex w-full items-center gap-3 rounded-lg px-2 py-1.5 text-left transition-colors hover:bg-zinc-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:hover:bg-zinc-800/60"
>
<span
aria-hidden="true"
className={`h-2.5 w-2.5 shrink-0 rounded-full transition-opacity ${c.dot} ${
off ? "opacity-25" : "opacity-100"
}`}
/>
<span
className={`flex-1 text-sm transition-colors ${
off
? "text-zinc-400 line-through dark:text-zinc-600"
: "text-zinc-800 dark:text-zinc-200"
}`}
>
{c.name}
</span>
<span className="text-xs tabular-nums text-zinc-500 dark:text-zinc-500">
{nf.format(c.sessions)}
</span>
<span
className={`w-12 text-right text-sm font-semibold tabular-nums ${
off
? "text-zinc-400 dark:text-zinc-600"
: "text-zinc-900 dark:text-zinc-100"
}`}
>
{share.toFixed(1)}%
</span>
</button>
</li>
);
})}
</ul>
<div className="mt-auto flex items-center justify-between gap-4 border-t border-zinc-200 pt-4 dark:border-zinc-800">
<p className="text-sm text-zinc-700 dark:text-zinc-300">
<span className="font-semibold tabular-nums text-zinc-900 dark:text-zinc-100">
{nf.format(visibleSessions)}
</span>{" "}
sessions shown
</p>
<p className="text-xs tabular-nums text-zinc-500 dark:text-zinc-500">
{visible.length} of {CHANNELS.length} channels
</p>
</div>
</Card>
{/* Deploy heatmap */}
<Card className="lg:col-span-3">
<div className="flex items-start justify-between gap-3">
<div>
<h3 className="text-sm font-medium text-zinc-600 dark:text-zinc-400">
Deploy activity
</h3>
<p className="mt-1 text-xs text-zinc-500 dark:text-zinc-500">
Last 28 days · {sum(heat.map((h) => h.count))} releases to
production
</p>
</div>
</div>
<div
role="group"
aria-label="Deploys per day, last 28 days"
className="mt-5 grid grid-cols-7 gap-1.5"
>
{heat.map((cell, i) => {
const selected = i === heatSel;
return (
<button
key={cell.label}
ref={(el) => {
heatRefs.current[i] = el;
}}
type="button"
tabIndex={i === heatFocus ? 0 : -1}
aria-pressed={selected}
aria-label={`${cell.label}: ${cell.count} ${
cell.count === 1 ? "deploy" : "deploys"
}`}
onClick={() => {
setHeatSel(i);
setHeatFocus(i);
}}
onFocus={() => setHeatFocus(i)}
onKeyDown={(e) => onHeatKeys(e, i)}
className={`aspect-square rounded-md transition-transform hover:scale-110 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-zinc-900 ${heatClass(
cell.count,
)} ${
selected
? "ring-2 ring-zinc-900 ring-offset-2 ring-offset-white dark:ring-zinc-100 dark:ring-offset-zinc-900"
: ""
}`}
/>
);
})}
</div>
<div className="mt-5 flex items-center justify-between gap-4 border-t border-zinc-200 pt-4 dark:border-zinc-800">
<p className="text-sm text-zinc-700 dark:text-zinc-300">
<span className="font-semibold text-zinc-900 dark:text-zinc-100">
{heat[heatSel]?.label}
</span>{" "}
· {heat[heatSel]?.count}{" "}
{heat[heatSel]?.count === 1 ? "deploy" : "deploys"}
</p>
<div className="flex items-center gap-1.5">
<span className="text-xs text-zinc-500 dark:text-zinc-500">
Less
</span>
{[0, 2, 4, 6].map((n) => (
<span
key={n}
aria-hidden="true"
className={`h-3 w-3 rounded-sm ${heatClass(n)}`}
/>
))}
<span className="text-xs text-zinc-500 dark:text-zinc-500">
More
</span>
</div>
</div>
</Card>
{/* Quarterly targets */}
<Card className="lg:col-span-6">
<h3 className="text-sm font-medium text-zinc-600 dark:text-zinc-400">
Quarterly targets
</h3>
<div className="mt-5 grid grid-cols-1 gap-6 sm:grid-cols-3">
{GOALS.map((goal, i) => (
<div key={goal.id}>
<div className="flex items-baseline justify-between gap-2">
<p className="text-sm font-medium text-zinc-800 dark:text-zinc-200">
{goal.name}
</p>
<p className="text-sm font-semibold tabular-nums text-zinc-900 dark:text-zinc-100">
{goal.progress}%
</p>
</div>
<div
className="relative mt-2 h-2 w-full overflow-hidden rounded-full bg-zinc-100 dark:bg-zinc-800"
role="progressbar"
aria-valuemin={0}
aria-valuemax={100}
aria-valuenow={goal.progress}
aria-label={`${goal.name}: ${goal.detail}`}
>
<motion.div
className={`relative h-full overflow-hidden rounded-full ${goal.bar}`}
initial={reduced ? false : { width: 0 }}
animate={{ width: `${goal.progress}%` }}
transition={{
duration: reduced ? 0 : 0.8,
delay: reduced ? 0 : 0.1 + i * 0.1,
ease: "easeOut",
}}
>
<span
aria-hidden="true"
className="dsw-sheen absolute inset-y-0 -left-1/3 w-1/3 bg-white/40"
/>
</motion.div>
</div>
<p className="mt-2 text-xs text-zinc-500 dark:text-zinc-500">
{goal.detail}
</p>
</div>
))}
</div>
</Card>
</div>
</div>
</section>
);
}Dependencies
Licence
Built by Web Innoventix. Free for personal and commercial use, no attribution required.
Built by Web Innoventix
Need a custom interface, a full website, or SEO that gets you cited by AI? We design, build and rank it end to end.
Get a free quoteSimilar components
Browse all →
Dashboard KPI Row
Originalrow of KPI stat cards with trends

Dashboard Activity
Originalrecent activity feed widget

Dashboard Chart Widget
Originalchart panel widget with tabs

Dashboard Revenue
Originalrevenue overview card with chart

Dashboard Users
Originalactive users widget

Dashboard Tasks
Originaltasks/checklist widget

Dashboard Calendar Widget
Originalmini calendar dashboard widget

Dashboard Notifications
Originalnotifications panel widget

Dashboard Overview
Originalcompact dashboard overview grid

Spotlight Hero
OriginalA centred hero with a soft radial spotlight, badge and dual call-to-action.

Split Hero
OriginalA two-column hero pairing a headline and CTAs with a product mock and social proof.

Gradient Spotlight Hero
OriginalA minimal centred hero with a soft gradient-mesh backdrop, announcement pill and dual call-to-action buttons.

