Compare periods Last week's line under today's. The comparison series has to read as secondary or the chart looks like two equal metrics, and the label must say which period the ghost is. npx shadcn@latest add toggle-group npm i recharts Tokens this needs: --foreground, --card, --muted, --muted-foreground, --border, --chart-1 ──────────────────────────────────────────────────────────────────────── // ComparePeriods.tsx ──────────────────────────────────────────────────────────────────────── import { Line, LineChart, XAxis, YAxis } from "recharts"; import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; /** * The base is the whole design decision, and there are three honest ones. * Each aligns a different cycle, so the chart says which it is using and * what that aligns, rather than leaving "last period" to mean anything. */ export const BASES = { "1d": { pick: "yesterday", legend: "same period yesterday", aligns: "time of day; a Sunday against a Monday" }, "7d": { pick: "a week ago", legend: "same period last week", aligns: "day of week and time of day" }, "1y": { pick: "a year ago", legend: "same period last year", aligns: "the season; the weekday wobbles" }, } as const; export type Base = keyof typeof BASES; const PRESENT = "hsl(var(--chart-1))"; const PAST = "hsl(var(--muted-foreground))"; export function ComparePeriods({ label, current, comparison, base, onBaseChange, width = 400, height = 150 }: { /** The current period, as the legend names it. */ label: string; current: number[]; /** The same window shifted back by `base`, on the same scale. */ comparison: number[]; base: Base; onBaseChange: (base: Base) => void; width?: number; height?: number; }) { const data = current.map((now, i) => ({ i, now, then: comparison[i] })); const sum = (xs: number[]) => xs.reduce((a, b) => a + b, 0); // The gap as a number. Two lines show a difference; only a figure says how big. const pct = Math.round(((sum(current) - sum(comparison)) / sum(comparison)) * 100); return (
aligns {BASES[base].aligns}