New
Analytics Dashboard
A pattern for composing KPI tiles, charts, and tables into a cohesive analytics screen with a sticky time-range header. Mirrors Warren's InsightsView and JobFlo's AnalyticsView.
Demo
Analytics
7 Days30 Days90 DaysAll Time
Overview
+12%
1,284
Applications
+3%
34%
Interview Rate
-1.4d
8.2d
Avg Response
+2
6
Active Pipelines
Stage Funnel
Applied1,284
Screening438
Interview182
Offer34
Hired12
Layout spec
| Zone | Spec |
|---|---|
| Sticky header | 56 px, view title + time-range Segmented Button, --background backing, bottom divider |
| KPI row | Equal-width tiles in HStack, gap --spacing-lg; collapses to 2-col on narrow |
| Primary chart zone | 1fr flexible chart + 300 px companion (donut or breakdown list) |
| Section headers | 11 px semibold, letter-spacing 0.8, --text-tertiary, uppercase — matches label-mono |
| Empty states | Inline EmptyState per chart tile; never full-page replacement |
| Dashboard background | --background, not --surface |
Time range picker
Use the Segmented Button locked to these options: 7 Days / 30 Days / 90 Days / All Time. Place it in the sticky header row, right-aligned. The picker drives all chart and KPI data on the same screen.
Implementation
AnalyticsDashboard.tsx
tsx
"use client";
import { useState } from "react";
type TimeRange = "7d" | "30d" | "90d" | "all";
interface KPIData {
label: string;
value: string;
delta?: string;
trend?: "up" | "down" | "flat";
icon?: React.ReactNode;
}
export function AnalyticsDashboard() {
const [range, setRange] = useState<TimeRange>("30d");
return (
<div style={{ display: "flex", flexDirection: "column", gap: 0 }}>
{/* Sticky header */}
<div style={{
position: "sticky",
top: 0,
height: 56,
display: "flex",
alignItems: "center",
justifyContent: "space-between",
padding: "0 20px",
background: "rgb(var(--background))",
borderBottom: "1px solid rgb(var(--border))",
zIndex: 10,
}}>
<h1 style={{ fontSize: 16, fontWeight: 700, margin: 0 }}>Analytics</h1>
<TimeRangePicker value={range} onChange={setRange} />
</div>
<div style={{ padding: 20, display: "flex", flexDirection: "column", gap: 20 }}>
{/* KPI row */}
<div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 12 }}>
<KPITile label="Total Applications" value="1,284" delta="+12%" trend="up" />
<KPITile label="Interview Rate" value="34%" delta="+3%" trend="up" />
<KPITile label="Avg. Response Time" value="8.2d" delta="-1.4d" trend="up" />
<KPITile label="Active Pipelines" value="6" delta="+2" trend="up" />
</div>
{/* Primary chart zone */}
<div style={{ display: "grid", gridTemplateColumns: "1fr 300px", gap: 12 }}>
<ChartTile title="Applications Over Time">
{/* Chart content */}
</ChartTile>
<ChartTile title="Stage Breakdown">
{/* Donut / breakdown */}
</ChartTile>
</div>
</div>
</div>
);
}
function TimeRangePicker({ value, onChange }: { value: TimeRange; onChange: (v: TimeRange) => void }) {
const options: [TimeRange, string][] = [["7d","7 Days"],["30d","30 Days"],["90d","90 Days"],["all","All Time"]];
return (
<div style={{ display: "flex", gap: 2, padding: 3, borderRadius: 8, background: "rgb(var(--surface-raised))", border: "1px solid rgb(var(--border))" }}>
{options.map(([key, label]) => (
<button key={key} onClick={() => onChange(key)} style={{
padding: "4px 12px", borderRadius: 6, fontSize: 12, fontWeight: 500,
border: "none", cursor: "pointer",
background: value === key ? "rgb(var(--accent))" : "transparent",
color: value === key ? "#fff" : "rgb(var(--text-secondary))",
transition: "background 0.15s",
}}>{label}</button>
))}
</div>
);
}