Dashboard UI skill for Claude Code
Every widget answers a question you can name. Claude designs dashboards from the questions users ask instead of the data that happens to exist. It picks the chart that fits each question, builds KPI cards with named comparison periods, right-aligns tabular numbers, keeps filters in the URL, and gives every widget its own loading, empty, error and stale state so one failed query never blanks the page.
- name
dashboard-ui - size183 lines · ≈2,150 tokens
- files1
- licenseMIT
- updated
What Claude sees before loading it
This description is the only part of the skill that sits in context all the time. Claude reads it to decide when to load the full playbook below.
description: Designs data-dense dashboards and admin screens — layout by the questions users ask, KPI cards with honest deltas, chart-type selection, readable data tables, filter bars with URL state, number formatting, and per-widget loading, empty, error, and stale states. Use when building an analytics dashboard, admin panel, reporting page, or internal tool, or when a dashboard is cluttered, hard to scan, or full of charts nobody reads. Try asking
- Design an analytics dashboard for our subscription metrics
- This admin dashboard is cluttered and nobody reads it. Redesign the layout
- Make our orders table easier to scan and add filters that persist in the URL
Use it when
- Building an analytics, reporting, or admin screen
- A dashboard is cluttered or hard to scan
- Choosing chart types for specific metrics
- Designing dense data tables with filters and bulk actions
Skip it when
- You only need state designs across the app, use empty-error-states instead
- The issue is slow rendering of large tables, use react-rerenders instead
How to check the result
Each widget maps to a written question, realistic messy data renders cleanly, and blocking one API request produces an isolated error state with retry.
The common mistake it prevents
Coloring deltas by direction instead of meaning, so a rising churn rate shows up in reassuring green.
A dashboard is done when each widget answers a named question, the most important number is the most prominent thing on screen, every number shows its unit, period, and comparison, tables scan in a single eye movement per column, and each widget handles loading, empty, error, and stale data on its own.
Ground rules
- Start from questions, not from available data. “Are signups on track this month?” produces a better widget than “signups table”.
- Chrome is neutral; color is reserved for data and status. A dashboard with a colored sidebar, colored cards, and colored charts has no signal left.
- One primary chart type per question. Decoration (3D, gradients in bars, donut thickness tricks) is noise.
- Filters and date ranges live in the URL so views are shareable and survive reload.
- Density is a feature for expert users: favor 13–14px UI text, 32–40px table rows, and tight but consistent spacing over marketing-style whitespace.
Avoid the generic look
| Default reflex | Better |
|---|---|
| Four identical KPI cards with icons in colored circles | KPI row with the hero metric larger; drop icons, show delta and sparkline instead |
| Every widget in a card with shadow | Flat panels separated by 1px borders or background steps; cards only where grouping is unclear |
| Rainbow pie/donut charts | Horizontal bar sorted by value; donut only for 2–3 parts of a whole |
| Gradient-filled area charts | Thin line (1.5–2px) plus light fill at 8–12% opacity, or no fill |
| Sidebar with 20 top-level items | ≤ 7 primary sections; the rest in grouped secondary nav or settings |
| “Welcome back, John!” banner eating the first 120px | Put the answer to the top question there |
Workflow
- [ ] 1. List users, their top 3–5 questions, and the decision each one drives
- [ ] 2. Map each question to a widget type
- [ ] 3. Lay out by priority: hero metrics → trends → breakdowns → detail tables
- [ ] 4. Build filter bar and URL state
- [ ] 5. Specify every widget's states
- [ ] 6. Format numbers, dates, units consistently
- [ ] 7. Verify with real (or realistic, ugly) data
2. Widget selection
| Question shape | Widget |
|---|---|
| “How much right now / vs. target?” | KPI card: value, delta vs. comparable period, optional sparkline or progress to target |
| “How is it changing over time?” | Line chart (continuous), column chart (≤ ~20 discrete periods) |
| “Which category is biggest?” | Horizontal bar, sorted descending, direct labels |
| “What share of the whole?” | Stacked bar (100%) or donut with ≤ 3 segments; never a pie with 8 slices |
| “How is it distributed?” | Histogram or box plot |
| “Is there a relationship?” | Scatter plot |
| “Where are the hotspots across two dimensions?” | Heatmap with a sequential single-hue scale |
| “Which exact records?” | Table with sort, filter, and row actions |
| “Did a flow leak?” | Funnel as horizontal bars with step conversion % |
3. Layout
- 12-column grid, 16–24px gaps. Hero KPIs top-left (first eye fixation), 3–5 KPIs max in the top row.
- Trend charts span 6–8 columns; breakdowns 4–6; tables full width.
- Keep the most-used filter controls in a single sticky bar under the page header.
- Page title + last-updated timestamp + date range visible without scrolling.
KPI card anatomy
Revenue (MTD) ← label, 13px, muted
$184,230 ← value, 28–36px, tabular-nums, weight 600
▲ 12.4% vs. same period last month ← delta: arrow + sign + %, period named
[sparkline, 30 days] ← optional, no axes, 32–40px tall
- Delta color follows meaning, not direction: a rise in churn is red. Encode the arrow too (color is never the only signal).
- Compare like with like: month-to-date vs. the same days last month, not vs. the full previous month.
- Show ”—” with a tooltip when a comparison is unavailable, never “0%” or “NaN%”.
Chart rules
- Bar/column axes start at zero. Line charts may start elsewhere if labeled clearly.
- ≤ 5–7 series per chart; highlight one, gray the rest when comparing.
- Direct labels beat legends; if a legend is needed, order it like the data.
- No dual y-axes; use two aligned small charts instead.
- Gridlines light (neutral at low contrast), 3–5 horizontal lines; no vertical gridlines for time series.
- Categorical palette: 6–8 hues of similar lightness (OKLCH L ≈ 0.6–0.7 on light, 0.7–0.8 on dark) so no series dominates; sequential scales use one hue varying L.
- Tooltips show the exact value, unit, and date; they are a supplement, not the only way to read values (provide a table view for accessibility).
Tables
| Rule | Value |
|---|---|
| Row height | 32px compact, 40px default, 48px comfortable (offer a density toggle) |
| Numbers | Right-aligned, font-variant-numeric: tabular-nums, same decimals per column |
| Text | Left-aligned; truncate with ellipsis + full value in title/tooltip |
| Header | Sticky, sortable columns show sort direction, 12–13px weight 600 |
| Zebra striping | Optional; prefer 1px row dividers; striping helps on very wide tables |
| Row actions | Visible on hover and on focus, plus an always-visible overflow menu for touch |
| Large data | Paginate (25/50/100) or virtualize beyond ~200 rendered rows |
| Selection | Checkbox column + bulk action bar that appears on selection |
4. Filters and URL state
// Read and write filters through the URL so views are shareable
const params = new URLSearchParams(location.search);
params.set("range", "last_30d");
params.set("region", "emea");
history.replaceState(null, "", `?${params}`);
- Date range presets first (Today, 7d, 30d, MTD, QTD, Custom); show the resolved dates and the timezone.
- Show active filters as removable chips plus a “Clear all”.
- Apply instantly for cheap queries; use an explicit Apply button when queries are slow.
5. Widget states (each widget independently)
| State | Design |
|---|---|
| Loading | Skeleton with the widget’s real shape; no full-page spinner |
| Empty | Say why (“No orders in this range”) and offer the fix (widen range, clear filter) |
| Error | Inline in the widget: what failed + Retry; the rest of the dashboard stays usable |
| Partial / sampled | Badge: “Sampled 10%” or “Incomplete: today’s data still processing” |
| Stale | “Updated 3h ago” in muted text; warning color if beyond the SLA |
| No permission | Explain and name who can grant access |
See empty-error-states for copy and patterns.
6. Formatting
const usd = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", maximumFractionDigits: 0 });
const compact = new Intl.NumberFormat("en-US", { notation: "compact", maximumFractionDigits: 1 }); // 1.2M
const pct = new Intl.NumberFormat("en-US", { style: "percent", maximumFractionDigits: 1 });
- Compact notation in KPI cards and axes; full precision in tables and tooltips.
- Always show units; label currencies when more than one exists.
- Dates: relative for recent events (“5 min ago”), absolute with timezone on hover.
Gotchas
- Averages of averages and percentages summed across segments mislead; compute from raw numerators and denominators.
- Auto-refresh that re-renders charts every few seconds resets hover and scroll; refresh data in place and keep interaction state.
- Truncated y-axes on bar charts exaggerate differences; reviewers notice.
- Empty dashboards for new accounts are the first impression; design them with sample data or setup steps.
- Timezone mismatches between server aggregation and the browser shift daily totals; state the timezone used for bucketing.
Verification
- Load with production-like data volumes, including long names, zeroes, nulls, negative values, and 10× the expected row count.
- For each widget, name its question; delete widgets that have no question.
- Simulate slow and failed API responses per widget (DevTools request blocking) and confirm isolated error states.
- Check keyboard access to sorting, filters, and row actions; charts have a data table alternative.
- Grayscale screenshot: status and deltas still distinguishable via arrows/labels.
Output contract
## Dashboard spec: <name>
| Question | Widget | Metric definition | Comparison | Owner |
Layout: KPI row (Revenue MTD, Active accounts, Churn %), trend (8 col), breakdown (4 col), table
Filters (URL params): range, region, plan
States: specified per widget (loading / empty / error / stale) About this skill
Written and maintained by the Claude Skills editors to the same editorial standard as every skill in the directory: a selective trigger, a real workflow, known pitfalls and a verification step. First published , last updated . MIT licensed.