Operate · Tidepool
Analytics dashboard
A product analytics overview went from a stock shadcn dashboard of eight identical KPI cards and a four-series area chart to a release readout that answers one question in words: did last week's onboarding checklist move activation, and is the number final yet?
- Persona
- Sam Okafor, Head of growth
- Their goal
- Check on Monday whether last week's onboarding checklist moved activation, and see where the funnel still leaks, without exporting to a spreadsheet.
- Lenses applied
- clarify, harden, distill, animate, typeset, adapt
/pristine design "redesign the overview dashboard"Before4.5/10

After9.5/10

Verdict
4.5/10 Level 6
Clean, tidy and interchangeable with every shadcn starter. Eight identical cards each say "+x% from last month", Signups and New Signups repeat, MRR shows up again as Revenue, the chart stacks four unrelated series with no y-axis, and nothing tells Sam whether the checklist worked.
9.5/10 Level 10
The page opens on a sentence, not a card grid: activation jumped to 41.8% the week the checklist shipped, 7.6 pts above an 11-week range drawn behind the line. The dashed tail and "Still counting" note say the cohort isn't final until Oct 27, the funnel shows which two steps moved, and stale Stripe data, an expired HubSpot token and a live event stream each say what they are and what to do.
Scores
Every Dimension, before and after.
| Dimension | Before | After | Change |
|---|---|---|---|
| Persona fit | 3 | 10 | |
| Hierarchy & clarity | 3 | 10 | |
| Layout & spacing | 6 | 9 | |
| Typography | 6 | 9 | |
| Color & theming | 5 | 9 | |
| Consistency | 6 | 9 | |
| Responsiveness | 5 | 9 | |
| Motion & interaction | 4 | 9 | |
| Accessibility | 4 | 9 | |
| Copy | 4 | 10 | |
| Robustness & performance | 3 | 10 |
The critique
What the red pen found.
Vibe-code tells
- Stock shadcn "Dashboard" starter layout
- Tabs for Overview / Analytics / Reports / Notifications
- Identical KPI cards with duplicate metrics
- "+x% from last month" under every number
- Default --chart-1…5 palette assigned by index
- Four unrelated series on one area chart
- Every widget in an identical rounded-xl card
- Unlabeled icon buttons and placeholder-only search
- outline: none on custom nav links
- Made-up vanity numbers
The page doesn't answer Sam's question
Activation is one of eight identical cards, shown as "+22.2% from last month" with no base. The chart has week labels but no y-axis and no marker for the checklist launch, so you can't connect the change to the number.
Duplicate and invented metrics
Signups and New Signups show different numbers. MRR and Revenue both appear. "Total Users 24,812" and "Growth +24.5%" have no definition or comparison period.
Chart palette encodes nothing
The default `--chart-1…5` orange, teal, navy and yellow are handed out by index: four unrelated series (Signups, Activated, Visitors, MRR on a different scale) share one area chart, and every funnel step gets its own color. Color is assignment order, not a signal for up, down or selected.
Every number has the same weight
Every KPI is `text-2xl font-bold` with a 12px `text-muted-foreground` delta line, so a vanity total and the metric Sam cares about look identical. Figures are proportional, so columns of numbers jitter.
Focus is removed and there are no states
The custom sidebar links set `outline: none` with only a hover background. The search box has a placeholder and no label, the trash button is an unlabeled icon, and there is no loading, empty or partial-data state, so a filter with no matches just shows a blank table.
Identical cards, 32px row actions
Twelve `rounded-xl border` cards with the same padding and shadow give no grouping or priority, and each event row carries three 32px buttons (View, Edit, delete) below the 44px target size.
The changes
Before, after, and why.
The report Pristine hands back after a run, with the real values from the two pages above.
| Lens | Before | After | Why |
|---|---|---|---|
| clarify | Activation "+22.2% from last month" as one of 8 cards | Release readout: "Activation jumped the week the checklist shipped, clear of its 11-week range." 41.8% vs. 33.1–34.2%, +7.6 pts | Sam's Monday question gets a sentence with its proof under it, before any chart. |
| harden | Last week plotted like every other week | Dashed provisional tail, hollow "41.8% so far" dot, "Still counting… Final read Mon, Oct 27" with a running mark | Signups from the last 7 days can still activate. The chart says the number is partial instead of letting it be over-read. |
| clarify | Four stacked area series in --chart-1…4, week labels, no y-axis | Shaded band for the previous 11 weeks' range, "Checklist shipped" marker, metric switch, View as table | The band is the baseline, so the jump reads against normal variation rather than one prior week. |
| distill | Funnel bars in a different chart color each | "Where it moved": step conversion bars with an 11-week-average tick, +8.2 / +4.7 pts deltas, checklist tags, 291-account leak callout | Shows which steps the checklist changed and where the next drop still is. |
| harden | Integrations shown as green "Connected" badges | Data sources with live / retrying / needs-you / synced marks; MRR KPI says "Stripe sync retrying · as of 06:10"; Reconnect HubSpot runs → done | A number from stale data says so next to the number, and the fix is one click away. |
| animate | transition-colors on buttons only; numbers swap with no change shown | clip-path draw-in 520ms, KPI values roll on range change, new events slide in every 6s; Pause queues them behind "Show 1 new event" | Motion shows what changed and where. Pausing keeps rows from moving while Sam reads. |
| distill | 8 identical KPI cards incl. Signups + New Signups, MRR + Revenue | 4 KPIs in one <dl>; Activation definition after a 500ms hover; W4 heatmap cells outlined as the source of the week-4 KPI | Each number appears once and you can see where it comes from. |
| typeset | .kpi .value { font-size: 24px; font-weight: 700 } ×8; 12px muted delta lines | 13px labels in --text-2, tabular-nums, JetBrains Mono for event names and properties | Labels meet contrast, numbers line up, and event payloads read as code. |
| harden | .sidebar a { outline: none }; unlabeled search and trash icon; blank table on no match | :focus-visible ring; "No events match" with Clear filter; dashed not-yet cohort cells | Keyboard users can reach every control, and missing data looks deliberately missing. |
| adapt | min-height: 100vh; 7-column events table scrolls sideways at 390px | 100dvh shell with a sticky sidebar; tables scroll inside .table-scroll; no page scroll at 320px | Sam checks this on a phone too. |