evals/cases.yaml
cases:
- id: dashboard_ux_audit_request
prompt: "I have an analytics dashboard with 12 KPI cards all the same size, 3 tabs hiding different data views, and a dense data table below. Users say they can't find what matters. What UX problems do you see and how should I fix the layout?"
fixtures: []
rubric:
- "identifies tab overuse as hiding information behind clicks"
- "flags flat visual hierarchy (12 equal-weight KPI cards)"
- "rates findings by severity"
- "suggests layout improvements with rationale grounded in UX principles"
- "references concepts like cognitive load, Shneiderman's mantra, or visual hierarchy"
trigger_expected: true
assertions:
- type: matches_regex
target: "(?i)(critical|major|minor|severity|finding)"
weight: 1.0
- type: matches_regex
target: "(?i)(hierarchy|cognitive|information architecture|visual weight|Shneiderman|overview first)"
weight: 0.8
- type: matches_regex
target: "(?i)(tab|hidden|click|interaction cost)"
weight: 0.8
- id: component_recommendation_request
prompt: "I'm building a B2B dashboard with Next.js and Tailwind CSS. I need charts for time-series metrics, a sortable data table for 500+ rows, and KPI cards. What UI libraries should I use and why?"
fixtures: []
rubric:
- "recommends specific libraries with rationale"
- "considers tech stack compatibility (Tailwind)"
- "addresses performance for large datasets"
trigger_expected: true
assertions:
- type: matches_regex
target: "(?i)(shadcn|recharts|tanstack|tremor|antd|MUI|nivo|ag.grid)"
weight: 0.8
- type: matches_regex
target: "(?i)(tailwind|bundle|performance|virtual)"
weight: 0.6
- id: negative_code_implementation
prompt: "Write the React component for a KPI card that shows revenue with a trend chart."
fixtures: []
rubric:
- "should not trigger — this is a code implementation request, not a UX review"
trigger_expected: false
- id: negative_general_design
prompt: "Design a new logo for our company."
fixtures: []
rubric:
- "should not trigger — this is graphic design, not UX audit"
trigger_expected: false
- id: negative_backend_review
prompt: "Review the architecture of our PostgreSQL schema and query performance."
fixtures: []
rubric:
- "should not trigger — this is backend architecture, not UX"
trigger_expected: false
references/audit-methodology.md
# UX Audit Methodology — Reference Guide
A systematic approach for auditing existing pages and features, producing prioritized, actionable findings.
---
## Table of Contents
1. [When to Run an Audit](#1-when-to-run-an-audit)
2. [The 8 Audit Dimensions](#2-the-8-audit-dimensions)
- 2.1 Information Architecture
- 2.2 Visual Hierarchy
- 2.3 Screen Real Estate
- 2.4 Interaction Cost
- 2.5 Cognitive Load
- 2.6 Context & Orientation
- 2.7 Data Presentation
- 2.8 Responsiveness & Edge Cases
3. [Severity Rating System](#3-severity-rating-system)
4. [Reading Code for UX Insight](#4-reading-code-for-ux-insight)
5. [Finding Report Template](#5-finding-report-template)
6. [Redesign Spec Template](#6-redesign-spec-template)
7. [Audit Checklist](#7-audit-checklist)
---
## 1. When to Run an Audit
Run a full audit when:
- A page has grown organically and users report confusion
- Preparing a redesign sprint — audit first, design second
- A feature lands in production but adoption is low
- Page has multiple competing sections with unclear priority
A targeted audit (2-3 dimensions only) is appropriate when:
- A specific complaint has been raised ("I can't find X")
- A single component is being refactored
Always audit against **real user goals**, not designer intent. The question is not "is this well-designed?" but "can the user accomplish their task efficiently and without confusion?"
---
## 2. The 8 Audit Dimensions
### 2.1 Information Architecture
**What it is**: The structure, labeling, and grouping of content.
**What to look for**:
- Is the primary task/metric shown first, or buried?
- Are related items grouped visually and semantically (same card, same section)?
- Are unrelated items accidentally grouped (proximity implies relationship)?
- Are labels using domain terms the user understands, or system/developer terms?
- Are any critical data points completely absent from the page?
- Is there redundant information shown in multiple places without purpose?
**Severity criteria**:
- Critical: Primary task requires hunting across multiple sections or is not present
- Major: Related data is split across unrelated areas; user must mentally join information
- Minor: Labels use jargon; groupings are imprecise but understandable
- Enhancement: Minor reordering would create a more logical scan path
**Common findings**:
- Summary KPIs buried below a large table
- "Total" figure placed below the detail it summarizes instead of above
- Actions (buttons) placed far from the data they act on
- Section headings that describe UI structure ("Panel A") rather than content ("Today's Output")
---
### 2.2 Visual Hierarchy
**What it is**: Whether the visual weight of elements matches their informational importance.
**What to look for**:
- What does the eye land on first? Is that the right thing?
- Are primary metrics visually larger/bolder/more contrasted than secondary metrics?
- Do decorative elements (borders, background fills, icons) compete with data?
- Is typography differentiated enough (size, weight) to signal importance levels?
- Are action buttons visually dominant relative to their importance?
- Are error/warning states visually prominent, or easy to miss?
**Severity criteria**:
- Critical: A misleading or dangerous value (e.g., negative balance, error state) is not visually distinct
- Major: The most important metric on the page has the same visual weight as secondary data
- Minor: Hierarchy exists but subtle; users slow down to parse it
- Enhancement: Tightening visual contrast or size ratios would improve scanning speed
**Common findings**:
- All table columns same width and weight — no column stands out as primary
- A single hero number (e.g., today's total) rendered in the same size as its breakdown rows
- Warning icons same color as neutral icons
- Primary CTA button styled same as secondary actions
---
### 2.3 Screen Real Estate
**What it is**: Whether the available space is used in proportion to content importance.
**What to look for**:
- Are large empty areas (whitespace waste) adjacent to dense, hard-to-read areas?
- Does a rarely-used feature take up a fixed large block while a critical feature is compact?
- Are cards or panels padded excessively at the expense of content density?
- Is anything forcing horizontal scroll on standard viewport widths?
- Are there collapsible or tabbed sections that hide content the user needs on every visit?
- Does a modal or drawer take over the full screen for a simple action?
**Severity criteria**:
- Critical: Key content is hidden behind an interaction (tab/scroll) on every page load for the primary workflow
- Major: A core metric requires scrolling past a large decorative or secondary section
- Minor: Padding is generous but does not hide content
- Enhancement: Tightening spacing would allow one more row or metric without scrolling
**Common findings**:
- A full-height sidebar with 3 items and large icons, consuming 30% of width
- Summary cards with 40px padding showing a 2-digit number
- A table that requires horizontal scroll because columns include rarely-needed fields
- Empty state illustration that takes up the full page body
---
### 2.4 Interaction Cost
**What it is**: The number of steps (clicks, scrolls, page changes, searches) required to reach key information or complete a task.
**What to look for**:
- How many clicks does the primary workflow take?
- Does the user have to leave the current page to get context they need to make a decision?
- Are frequently repeated actions exposed directly, or buried in menus?
- Does a form require filling out fields that could be auto-populated or defaulted?
- Do confirmation dialogs appear for low-risk actions?
- Is pagination forcing navigation for data the user needs all at once?
**Severity criteria**:
- Critical: Primary task requires 5+ steps when 1-2 is feasible
- Major: Common context-switching (e.g., check a value on page A to fill a form on page B) with no way to avoid it
- Minor: An action requires one extra click that could be removed
- Enhancement: Keyboard shortcuts or quick-add patterns would accelerate power users
**Common findings**:
- "Add entry" requires navigating to a separate page, filling a form, then navigating back
- Filters reset on page reload, requiring re-application every session
- A drill-down that opens a new page when a detail panel would suffice
- Date picker defaulting to today minus one year instead of today
---
### 2.5 Cognitive Load
**What it is**: The mental effort required to understand, parse, and act on the page.
**What to look for**:
- Are there more than 7+/-2 items in any list or menu without grouping?
- Is all data shown flat, with no progressive disclosure for detail?
- Are multiple different charts/tables showing overlapping data without clear differentiation?
- Does the page require the user to remember information from one section to apply it in another?
- Are there unlabeled icons that require learning?
- Does the page show data for states that don't apply (e.g., future period data when period is not open)?
**Severity criteria**:
- Critical: Page presents contradictory or ambiguous values that require external knowledge to interpret
- Major: User must hold multiple pieces of information in working memory simultaneously to accomplish the task
- Minor: A section is dense but parseable with effort
- Enhancement: Progressive disclosure or inline guidance would smooth the learning curve
**Common findings**:
- A dashboard showing 12 KPI cards with no grouping or priority signal
- A table with 15 columns, most of which are rarely needed
- Two charts on the same page showing different views of the same data with no explanation of how they relate
- Status indicators using color alone (inaccessible) with no label or legend
---
### 2.6 Context & Orientation
**What it is**: Whether the user always knows where they are, what time period they are viewing, what filters are active, and what state the system is in.
**What to look for**:
- Is the active date/period always visible without scrolling?
- Are active filters displayed persistently (as chips or a summary bar)?
- Is the current page/section indicated in navigation?
- After an action (save, delete, submit), does the UI confirm what happened?
- When data is stale or cached, is this communicated?
- On drill-down pages, is there a breadcrumb or back-path?
**Severity criteria**:
- Critical: User cannot tell which time period or entity they are viewing data for — data could be misread
- Major: Active filters are not visible; user does not realize data is filtered
- Minor: Breadcrumb is present but inaccurate or incomplete
- Enhancement: Timestamp of last data refresh would increase trust
**Common findings**:
- A data table with no indication of which month/period it covers
- Filters applied in a sidebar that closes after application — active filters are invisible
- A success toast that disappears in 2 seconds with no persistent confirmation
- A loading state with no indication of what is loading or how long it will take
---
### 2.7 Data Presentation
**What it is**: Whether data is shown in the right format, chart type, and level of precision.
**What to look for**:
- Are comparisons (trend, target vs actual) shown in a way that makes the comparison obvious?
- Are large numbers formatted with thousands separators, units, and appropriate precision?
- Is a bar chart used when a line chart would better show trend over time?
- Are percentages shown alongside absolutes where both matter?
- Are empty/null values displayed as blank, zero, or "---"? Is it consistent?
- Is negative/bad data visually distinguished from positive/good data (e.g., red/green, down arrow)?
- Are dates formatted consistently and in the user's locale?
**Severity criteria**:
- Critical: Chart type or scale distorts the data (e.g., a pie chart with 12 slices; a bar chart with non-zero baseline making a small difference look large)
- Major: Numbers lack units or formatting, requiring mental calculation
- Minor: Inconsistent formatting (some numbers with commas, some without)
- Enhancement: Adding a trend indicator (up/down arrow + delta) would speed comprehension
**Common findings**:
- Large numbers shown as "11000" instead of "11,000"
- A pie chart used for a time series
- A table mixing different units in the same column without a unit column
- "0" shown for days with no data, indistinguishable from actual zero-value days
- Percentage precision: "31.2847%" when "31.3%" is sufficient
---
### 2.8 Responsiveness & Edge Cases
**What it is**: How the page behaves at the boundaries — empty data, loading, errors, mobile viewports, long text.
**What to look for**:
- Is there a meaningful empty state (first-use, no results, no data for period)?
- Are loading states present and informative?
- Are error states actionable (does the user know what to do)?
- Does the layout break on mobile or narrow viewports?
- What happens with very long names/values in table cells?
- What happens when there are 0, 1, 2, and 200+ rows in a table?
- Are there any hardcoded heights that clip content?
**Severity criteria**:
- Critical: An error state shows a raw stack trace or blank screen with no guidance
- Major: Empty state for the primary list/table shows nothing — user doesn't know if data is missing or if there's a bug
- Minor: Long text is clipped with no tooltip; information is lost
- Enhancement: Empty states could include a call-to-action (e.g., "No entries yet — add the first one")
**Common findings**:
- Empty table with no message — blank white space
- Skeleton loader that never resolves if the API errors — spinner runs forever
- A card layout that stacks to single column on mobile but the stacked order is wrong (action before context)
- A name column that truncates long names to just a few characters
---
## 3. Severity Rating System
| Severity | Definition | Action |
|----------|-----------|--------|
| **Critical** | Users cannot accomplish their primary task, or are actively misled by the UI (wrong data appears correct, correct data is invisible) | Fix before release or as hotfix |
| **Major** | Significant friction or confusion. Key information is buried, misrepresented, or requires undue effort to access | Fix in current sprint |
| **Minor** | Suboptimal but functional. Improvement enhances experience without blocking the task | Include in polish pass |
| **Enhancement** | Delight-level polish. No user is struggling, but improvement would increase confidence or speed | Backlog — pick up when bandwidth allows |
When multiple findings interact (e.g., buried data + no orientation = can't find + don't know what you're looking at), escalate the combined severity by one level.
---
## 4. Reading Code for UX Insight
### Layout Structure in React
Look for the outermost container to understand grid/flex structure:
```tsx
// Tells you: 2-column layout, first col wider
<div className="grid grid-cols-3 gap-4">
<div className="col-span-2"> ... </div> // main content
<div> ... </div> // sidebar
</div>
```
Check for hardcoded heights that may clip content:
```tsx
// Risk: clips content at exactly 400px
<div className="h-[400px] overflow-hidden">
```
Check for `overflow-x-auto` or `overflow-x-scroll` — signals a component that may cause horizontal scroll.
### Conditional Rendering — What Gets Hidden
Look for conditions that hide entire sections:
```tsx
// Audit question: does the user know this section is hidden?
{someCondition && <ImportantSection />}
// Audit question: is the empty state meaningful?
{data.length === 0 ? <EmptyState /> : <DataTable rows={data} />}
```
If `<EmptyState />` is just `null` or `<div />`, that is a Major finding.
### State Management — What Controls Visibility
Identify what state drives the UI:
```tsx
const [activeTab, setActiveTab] = useState('summary')
const [filters, setFilters] = useState({ period: null, site: null })
```
For `activeTab`: are any tabs hiding primary-task data? Tabs add interaction cost.
For `filters`: if filters are in local state (not URL params), they reset on navigation — context loss.
### Data Flow — API to Display
Trace the data path to find where things can go wrong:
1. Find the API call (usually `useQuery`, `useSWR`, `useEffect + fetch`)
2. Find where the response is mapped to display values
3. Look for transformations: are units converted, values rounded, nulls handled?
```tsx
// Audit questions at each step:
const { data, isLoading, error } = useQuery(...)
// ^ is error handled? ^ is loading state shown?
const rows = data?.items.map(item => ({
...item,
total: item.trips * 22, // hardcoded conversion — flag for domain review
grade: item.grade?.toFixed(1) // what if grade is null?
}))
```
### Identifying Redundant UI Elements
Search for components rendered but conditionally invisible, or rendered with empty data:
```tsx
// If `stats` is always empty on this page, this card renders as blank
<StatsCard title="Weekly Summary" data={stats} />
```
Cross-reference: if a component is present in the file but `stats` always comes back `[]` from the API for this route, that is a screen real estate finding.
### Responsive Breakpoints
Look for Tailwind responsive prefixes or CSS media queries:
```tsx
// md: breakpoint at 768px
<div className="flex-col md:flex-row">
```
Trace what happens below the breakpoint — does stacking order make sense? On mobile, context should appear before action.
---
## 5. Finding Report Template
Use this format for every finding in the audit report.
```
## Finding: [Short Descriptive Name]
**Dimension:** [Information Architecture | Visual Hierarchy | Screen Real Estate |
Interaction Cost | Cognitive Load | Context & Orientation |
Data Presentation | Responsiveness & Edge Cases]
**Severity:** [Critical | Major | Minor | Enhancement]
**File(s):** [Component file path(s) where the issue lives]
**Current:**
[Describe what exists today. Be specific — quote labels, describe layout, note line numbers if relevant.]
**Problem:**
[Why this is a problem, grounded in a UX principle or user impact. Avoid "it looks bad" — describe the failure mode: what does a user misunderstand, miss, or struggle to do?]
**Recommendation:**
[Specific, implementable change. Not "improve hierarchy" — instead "move the daily total card above the breakdown table and increase font size to 24px/semibold".]
**Principle:**
[The UX principle behind the recommendation, e.g.:
- Fitts's Law: targets should be sized proportionally to their use frequency
- Miller's Law: 7+/-2 items in working memory
- Progressive Disclosure: show only what is needed for the current task
- Signal-to-Noise: remove elements that do not carry information
- Recognition over Recall: show context rather than requiring the user to remember it]
```
### Example Finding
```
## Finding: Period Label Missing from Data Table
**Dimension:** Context & Orientation
**Severity:** Critical
**File(s):** frontend/src/pages/ProductionPage.tsx, line 84
**Current:**
The data table renders without any visible header indicating which
operational period the data belongs to. The period is fetched in state but only
used to gate the "Add Entry" button.
**Problem:**
A user viewing historical data has no indication they are looking at January vs
February. If they navigate from a February page to a January page,
the table looks identical. They may enter data believing they are in the current
period when they are not, or draw incorrect conclusions from the data.
**Recommendation:**
Add a persistent period badge at the top of the table header:
"Viewing: January 2026 [CLOSED]" with the period status color-coded
(green = OPEN, grey = CLOSED). This should be sticky on scroll.
**Principle:**
Recognition over Recall — the user should not have to remember which period
they navigated from. The UI should make current context visible at all times.
```
---
## 6. Redesign Spec Template
Use this template to turn audit findings into an implementable design spec. One spec per page or major component redesign.
```markdown
# Redesign Spec: [Page/Component Name]
**Based on audit findings:** [list finding names]
**Target:** [what problem this spec solves in one sentence]
---
## Layout Wireframe
Replace with ASCII wireframe of the proposed layout.
Use boxes to represent sections. Label each box with its content type.
Example:
+-----------------------------------------------------+
| [Page Title] [Period Badge: Jan 2026 OPEN] |
+------------------------+----------------------------+
| HERO METRIC | SECONDARY METRICS (3) |
| Today's Output | [Target] [MTD] [Variance] |
| 1,240 units ^ 12% | |
+------------------------+----------------------------+
| [Active Filters Bar: Site: All | Shift: All | X ] |
+-----------------------------------------------------+
| DATA TABLE |
| [Date] [Shift] [Team] [Count] [Total] [Actions] |
| ... |
| [Pagination or Load More] |
+-----------------------------------------------------+
| [Empty State — only shown when table is empty] |
+-----------------------------------------------------+
---
## Component Inventory
List every component needed. Mark as New / Modified / Unchanged / Removed.
| Component | Status | Notes |
|-----------|--------|-------|
| PageHeader | Modified | Add period badge alongside title |
| HeroMetricCard | New | Large-format card for primary KPI |
| SecondaryMetricsRow | New | 3-up row of smaller metric cards |
| ActiveFiltersBar | New | Persistent display of active filters |
| DataTable | Modified | Remove 3 low-use columns; add unit column |
| EmptyState | New | Replaces null render |
| PaginationBar | Unchanged | |
---
## Data Requirements
For each data point shown, specify the source.
| Display Value | Source | Transformation | Null Behavior |
|---------------|--------|---------------|---------------|
| Today's Output | GET /output?date=today | sum(count) | Show "---" |
| Target | GET /targets?period=current | daily_target field | Show "No target set" |
| Variance % | Derived | (actual - target) / target * 100 | Hide if no target |
| Period Label | Context from period store | period.name + period.status | Fallback: "No period" |
---
## Interaction Specifications
Describe behaviors that require code beyond layout.
- **Filter persistence**: Active filters stored in URL params (`?site=1&shift=DAY`).
Restoring URL restores filter state. Filters survive page refresh.
- **Period badge click**: Opens period selector drawer (existing component).
Drawer closes on selection; page reloads with new period context.
- **Empty state CTA**: "Add First Entry" button routes to /data/add
with current period pre-populated.
- **Negative variance**: Variance value rendered in red with a down-arrow icon.
Positive variance rendered in green with up-arrow. Zero: neutral, no icon.
---
## Responsive Behavior
| Viewport | Layout Change |
|----------|--------------|
| < 768px (mobile) | Hero metric full width. Secondary metrics collapse to 2-up then 1-up. Table gains horizontal scroll. Filters collapse to a "Filters (2)" button. |
| 768-1024px (tablet) | Hero + secondary metrics side by side. Table shows 5 columns max. |
| > 1024px (desktop) | Full layout as wireframe above. |
---
## Edge Cases
| Scenario | Expected Behavior |
|----------|------------------|
| No entries for period | Empty state with message and CTA |
| API error on load | Error card with retry button; no blank screen |
| 200+ rows | Paginate at 50 rows; show "Showing 1-50 of 212" |
| Very long name | Truncate at 24 chars with tooltip showing full name |
| Period is CLOSED | Hide "Add Entry" button; show "Period closed" badge in red |
| Zero value day (legitimate) | Show row with 0, not blank; distinguish from missing data |
```
---
## 7. Audit Checklist
Use this checklist before declaring an audit complete.
### Coverage
- [ ] All 8 dimensions reviewed
- [ ] Every section/tab/state of the page has been examined (not just the happy path)
- [ ] Both mobile and desktop viewports considered
- [ ] Empty state, loading state, and error state reviewed
### Findings Quality
- [ ] Every finding specifies a dimension and severity
- [ ] Every finding has a specific recommendation (not just "improve X")
- [ ] Critical findings are anchored to a specific user task that fails
- [ ] No finding is purely aesthetic without a user impact
### Code Review
- [ ] Hardcoded values flagged (magic numbers, hardcoded strings)
- [ ] Null/undefined handling checked for every displayed value
- [ ] Conditional rendering traced — nothing important hidden unexpectedly
- [ ] Filter/state reset behavior confirmed (URL params vs local state)
### Output
- [ ] Findings sorted by severity (Critical first)
- [ ] Total count by severity noted (e.g., "2 Critical, 4 Major, 6 Minor, 3 Enhancement")
- [ ] Redesign spec written for any section requiring structural changes
- [ ] Quick wins (< 1 hour fixes) called out separately
references/component-libraries.md
# UI Component Libraries — B2B SaaS Dashboard Reference
A practical reference for selecting and combining UI libraries in data-heavy dashboards.
Last updated: 2026-03.
---
## Table of Contents
1. [Full Design Systems](#1-full-design-systems)
- Ant Design (antd)
- Material UI (MUI)
- Chakra UI
- shadcn/ui
2. [Specialized Dashboard/Data Components](#2-specialized-dashboarddata-components)
- Recharts
- Nivo
- TanStack Table
- AG Grid
- Tremor
3. [Premium/Design-Forward Libraries](#3-premiumdesign-forward-libraries)
- 21st.dev
- Magic UI
- Aceternity UI
- When premium is worth it
4. [Micro-Libraries for Specific Needs](#4-micro-libraries-for-specific-needs)
5. [Component Selection Decision Framework](#5-component-selection-decision-framework)
6. [Component Composition Patterns](#6-component-composition-patterns)
---
## 1. Full Design Systems
### Ant Design (antd)
**Best for**: Internal tools, admin panels, B2B dashboards — especially teams with a backend/full-stack focus. Huge component coverage out of the box.
**Strengths**
- Most complete component set of any open-source library (~70+ components)
- First-class table, form, and tree components
- ProComponents package (`@ant-design/pro-components`) adds ProTable, ProForm, ProLayout — near zero-config dashboard scaffolding
- Design tokens system in v5 makes theming reliable
**Version differences**
- v4 -> v5: CSS-in-JS (no more Less), design tokens, component API simplifications. Breaking changes are real — don't mix v4 and v5.
- v5 ProComponents: `ProTable` handles server-side pagination, filtering, and column configuration with a single `request` prop — it is the most powerful free data table outside AG Grid.
**Best dashboard components**
```
ProLayout — sidebar + header layout shell
ProTable — server-side data table with search toolbar
StatisticCard — KPI card with trend arrows
Statistic — number display with prefix/suffix
Timeline — activity feed / audit log
Descriptions — key-value detail panels
Tabs + Card — tabbed section containers
```
**Watch out for**
- Bundle size is large (~500KB gzipped with treeshaking). Use babel-plugin-import.
- Default styling looks "enterprise Chinese product" — requires token overrides for modern feel.
- Not ideal if you need heavy animation or a design-forward UI.
---
### Material UI (MUI)
**Best for**: Teams that want Google's design language, or are building something that needs to feel familiar/safe to enterprise buyers.
**Strengths**
- Excellent theming system — `createTheme` with palette, typography, component overrides
- `DataGrid` (free) and `DataGridPro` (paid) are production-grade
- MUI X suite: charts, date pickers, tree view — same theming contract as core
- Strong accessibility (WAI-ARIA) by default
**Dashboard-specific components**
```
DataGrid / DataGridPro — sortable, filterable, virtual scroll tables
MUI X Charts — bar, line, pie, scatter (v7+, stable)
Card + CardHeader — standard KPI layout
Drawer + AppBar — nav shell
Skeleton — loading states
LinearProgress — progress bars
Tooltip — hover context
```
**When it shines**
- You need a consistent design system across many product surfaces
- DataGridPro is worth the license ($180/dev/yr) for complex table requirements
- Pairing with `@mui/x-date-pickers` keeps date logic inside the same design token system
**Watch out for**
- `sx` prop performance: every `sx` call generates a CSS class at runtime. Use `styled()` or `classes` for hot paths.
- Theming requires deep knowledge to override reliably — don't fight the cascade.
---
### Chakra UI
**Best for**: Teams that want design flexibility without a full CSS overhaul. Excellent for rapid prototyping and custom design systems built on top.
**Strengths**
- Composable primitive components (Box, Flex, Stack, Grid) — layout is first-class
- Style props on every component (`p`, `mt`, `bg`, `color`) — no separate stylesheet needed
- `useColorMode` — dark/light toggle built in
- v3 (Ark UI under the hood) has headless-style composability
**Dashboard patterns**
```tsx
// KPI Card pattern
<Card>
<CardBody>
<Stat>
<StatLabel>Production Today</StatLabel>
<StatNumber>1,204 MT</StatNumber>
<StatHelpText>
<StatArrow type="increase" />
12% vs yesterday
</StatHelpText>
</Stat>
</CardBody>
</Card>
```
**When to use**
- Product needs a unique visual identity (Chakra stays out of the way)
- Small to mid-size team where writing full CSS is slower than style props
- Combine with Recharts/Nivo — Chakra doesn't ship charts
**Watch out for**
- No data table, no charts, no date picker — you will combine libraries
- v2 -> v3 is a significant migration (Ark UI architecture change)
---
### shadcn/ui
**Best for**: React + Tailwind projects. Modern default for new Next.js apps as of 2024-2026.
**Why it's gaining traction**
- Not a library you install — you copy components into your repo. Full ownership, no version lock.
- Built on Radix UI primitives (headless, accessible) + Tailwind CSS
- `npx shadcn-ui@latest add button` copies the component source directly
- Every component is customizable because you own the code
**Best dashboard components**
```
Card — clean container with header/content/footer slots
Table — basic HTML table with Tailwind styling
Badge — status pills (great for OPEN/CLOSED, Active/Inactive)
Select + Combobox — filter dropdowns
Sheet — slide-out detail panels
Dialog — confirmation modals
Skeleton — loading states (built in)
Command — keyboard-navigable search/filter palette
Tabs — section switching
```
**Composition model**
```tsx
<Card>
<CardHeader>
<CardTitle>Resource Usage</CardTitle>
<CardDescription>Last 30 days by category</CardDescription>
</CardHeader>
<CardContent>
{/* chart goes here */}
</CardContent>
<CardFooter>
<Button variant="outline">View Report</Button>
</CardFooter>
</Card>
```
**Watch out for**
- No charts — pair with Recharts or Tremor
- Requires Tailwind CSS — don't use if the project uses CSS Modules or CSS-in-JS
- Component APIs change between shadcn releases since you own the code — check changelogs before copying new components into existing projects
---
## 2. Specialized Dashboard/Data Components
### Recharts
**Install**: `npm install recharts`
**Bundle**: ~150KB gzipped
**Best chart types**
```
AreaChart — time-series trends (metrics over time)
BarChart — comparisons (plan vs actual)
ComposedChart — mix bar + line on same chart (actuals vs target)
PieChart — composition (category split)
RadarChart — multi-KPI spider charts
Treemap — hierarchical breakdowns
```
**Key customization points**
```tsx
<LineChart data={data}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="date" />
<YAxis />
<Tooltip content={<CustomTooltip />} /> {/* custom HTML tooltip */}
<Legend />
<Line type="monotone" dataKey="actual" stroke="#2563eb" dot={false} />
<Line type="monotone" dataKey="target" stroke="#94a3b8" strokeDasharray="4 4" />
<ReferenceLine y={target} stroke="red" label="Target" />
</LineChart>
```
**Performance**: ResponsiveContainer re-renders on every resize. For 10K+ data points, downsample before passing to chart. Recharts is SVG-based — Canvas-based alternatives (Chart.js) handle larger datasets.
**When to use**: Default choice for dashboards. Great API, good docs, no license cost.
---
### Nivo
**Install**: `npm install @nivo/core @nivo/bar` (per-chart packages)
**Bundle**: Heavier than Recharts (~60KB per chart type)
**When to use over Recharts**
- Need D3-quality animations on chart transitions
- Heatmaps (`@nivo/heatmap`) — activity calendars, shift patterns
- Sunburst / Treemap with better visual defaults
- Waffle charts (`@nivo/waffle`) — plan attainment visualization
**Drawback**: Larger bundle, slower build times. Only use the packages you need.
---
### TanStack Table (v8)
**Install**: `npm install @tanstack/react-table`
**Bundle**: ~14KB gzipped — headless, no styles
**Core features**
- Sorting, filtering, pagination — client-side and server-side
- Column visibility, column ordering
- Row selection (checkbox)
- Grouping and aggregation
- Virtual scrolling via `@tanstack/react-virtual`
**Production pattern for server-side table**
```tsx
const table = useReactTable({
data,
columns,
state: { sorting, columnFilters, pagination },
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
onPaginationChange: setPagination,
manualSorting: true, // tell table we sort on server
manualFiltering: true,
manualPagination: true,
pageCount: totalPages,
getCoreRowModel: getCoreRowModel(),
})
```
**When to use**: Best choice when you need full control over table rendering and combining with shadcn/ui or Chakra. Pairs well with any styling system.
**When NOT to use**: If you need Excel-like cell editing, column freezing, or pivot — use AG Grid.
---
### AG Grid
**Install**: `npm install ag-grid-community ag-grid-react`
**License**: Community (free) / Enterprise ($)
**Community free features**
- Sorting, filtering, pagination
- Row grouping (one level)
- CSV export
- Cell rendering / editing
**Enterprise features** (license required)
- Server-side row model (stream 1M+ rows)
- Excel export
- Column pivoting
- Multi-level row grouping
- Tree data
**When it's worth the enterprise license**
- Reports that users will use like a spreadsheet
- Large data sets (>50K rows rendered)
- Users expect Excel-like behavior (copy-paste, cell selection)
- Finance/accounting modules
**When community is enough**
- Operational tables (logs, records) — usually <500 rows per view
- Basic sort/filter is all that's needed
---
### Tremor
**Install**: `npm install @tremor/react`
**Purpose-built for dashboards** — wraps Recharts + Tailwind with dashboard-specific components.
**Best components**
```
AreaChart, BarChart, DonutChart — pre-styled, zero config
Card, Metric — KPI cards
ProgressBar, ProgressCircle — plan attainment
BadgeDelta — trend indicators (+12%, up)
DateRangePicker — reporting date range selection
MultiSelect — filter checkboxes
Tab, TabGroup — section navigation
Table — pre-styled data table
Tracker — period/status blocks (uptime, shift status)
```
**Quick KPI card**
```tsx
<Card>
<Text>Revenue Today</Text>
<Metric>$12,040</Metric>
<BadgeDelta deltaType="increase">12% vs yesterday</BadgeDelta>
<ProgressBar value={72} label="72% of daily target" />
</Card>
```
**When to use**: Fastest path to a good-looking dashboard if you're on Tailwind. Tremor components look polished by default. Good for MVPs.
**Watch out for**: Less flexible than building with shadcn/ui + Recharts directly. Theming is limited to their color palette system.
---
## 3. Premium/Design-Forward Libraries
### 21st.dev
**What it is**: Curated registry of high-quality React + Tailwind components, similar model to shadcn/ui — copy components into your project.
**Best use cases**
- Marketing-style dashboard hero sections
- Onboarding flows and empty states
- Navigation patterns (command palettes, side nav with icons)
- Complex filter UIs
**When to use**: When the default shadcn/ui aesthetic isn't distinctive enough and you want curated, design-reviewed components without building from scratch.
---
### Magic UI
**What it is**: Animation-first component library. Built on Framer Motion + Tailwind.
**Notable components**
```
AnimatedNumber — smooth number transitions (KPI changes)
NumberTicker — count-up animation
BorderBeam — animated border highlight
Shimmer Button — premium CTA buttons
Animated Gradient Text
Blur Fade — entrance animations for cards/sections
```
**When to use**
- Investor demos or onboarding flows that need to feel alive
- "Hero" KPI numbers on an executive dashboard
- Empty state illustrations with animation
**When it's over-engineered**
- Day-to-day operational data entry screens — animation adds cognitive noise
- Any screen where users are inputting data repeatedly — motion fatigue
---
### Aceternity UI
**What it is**: Design-forward React components with modern CSS effects (glassmorphism, 3D transforms, spotlight effects).
**Notable components**
```
Spotlight — cursor-following light effect on cards
Background Beams — animated background gradients
3D Card Effect — perspective tilt on hover
Glowing Stars — decorative backgrounds
Moving Border — animated card borders
```
**When to use**: Landing pages, login screens, marketing-facing dashboards shown to prospects.
**When it's wrong**: Internal operational tools used 8 hours/day. 3D effects and animation drain focus and battery on laptops.
---
### When Premium Components Are Worth It
**Worth it**
- Login/onboarding — first impression for new users, used rarely
- Executive/investor dashboard — needs to look impressive
- Marketing site — seen before users sign up
- One or two "hero" moments in product (milestone notifications, celebrations)
**Not worth it**
- Data entry forms (logs, records, submissions)
- CRUD tables used daily by ops staff
- Any screen with >5 interactive elements — animation competes with cognition
- Mobile use — complex effects often perform poorly
---
## 4. Micro-Libraries for Specific Needs
### Sparklines (inline charts)
| Library | Size | Best for |
|---------|------|----------|
| `react-sparklines` | ~8KB | Quick inline trend lines in table cells |
| `recharts` (small `LineChart`) | Larger | When already using Recharts |
| SVG hand-rolled | 0KB | Simple bars/lines — 20 lines of code |
For table cells showing 7-day trend: hand-rolled SVG sparkline is often better than adding a dependency.
---
### Number Animation
| Library | Notes |
|---------|-------|
| `countup.js` / `react-countup` | Count-up from 0 to value on mount. Good for KPI hero numbers. |
| `react-spring` | Full physics animation, `useSpring` for number interpolation. More control, larger bundle. |
| `framer-motion` | `useMotionValue` + `animate` — if already using Framer. |
| CSS `counter-reset` | CSS-only, zero JS. Limited to integers. |
**Recommendation**: `react-countup` for a quick KPI dashboard. Framer Motion if already in the project.
---
### Skeleton Loaders
| Approach | Notes |
|----------|-------|
| `shadcn/ui Skeleton` | Tailwind pulse animation. Copy into project. |
| `react-loading-skeleton` | Configurable widths, dark mode support, good defaults. |
| MUI `Skeleton` | If already on MUI — same theming system. |
| CSS-only | `animate-pulse` (Tailwind) on a div is often enough. |
**Rule of thumb**: Match skeleton shape to actual content layout. A skeleton that's 3 rows when real data is 3 rows looks intentional; a generic "bar" looks lazy.
---
### Date Pickers
| Library | Notes |
|---------|-------|
| `@mui/x-date-pickers` | Best if on MUI. Full calendar, range picker, time. |
| `react-day-picker` | Headless + light (~25KB). Pairs well with shadcn/ui. |
| `shadcn/ui Calendar` (built on react-day-picker) | Good default for Tailwind projects. |
| `flatpickr` / `react-flatpickr` | Standalone, no React dependency — good for non-React forms. |
**For date range filtering on dashboards**: `react-day-picker` with shadcn/ui Popover wrapper is the recommended pattern for Tailwind projects.
---
### Multi-Select / Tag Inputs
| Library | Notes |
|---------|-------|
| `cmdk` (Command) | Keyboard-driven, used in shadcn/ui Combobox. |
| `react-select` | Most full-featured: async, creatable, multi, group options. Still industry standard. |
| `downshift` | Headless, build your own UI on top. Maximum flexibility. |
| shadcn/ui `MultiSelect` | Community recipe — copy from shadcn/ui docs. Good enough for most cases. |
**For filtering dashboards**: `react-select` with `isMulti` remains the fastest implementation path despite being older.
---
### Tooltips and Popovers
| Library | Notes |
|---------|-------|
| `Radix UI Tooltip` | Accessible, used by shadcn/ui. Zero style, max control. |
| `Floating UI` (Popper.js successor) | Positioning engine used by most tooltip libraries under the hood. |
| `@tippyjs/react` | Full-featured: arrow, theme, trigger control. Good when Radix is not in stack. |
| Recharts built-in `<Tooltip>` | Use `content={<CustomTooltip />}` for full control. |
---
## 5. Component Selection Decision Framework
### Choosing a Library
**Step 1: What's already in the project?**
- Tailwind CSS -> shadcn/ui + Recharts + TanStack Table is the natural stack
- CSS-in-JS or plain CSS -> MUI or Chakra
- Heavy admin panel -> consider antd ProComponents
- No CSS framework -> pick one first, then choose UI library
**Step 2: Bundle size check**
```
shadcn/ui — ~0KB (you own the code, treeshakeable)
Recharts — ~150KB gzipped
Tremor — ~200KB gzipped (includes Recharts)
antd — ~500KB (use babel-plugin-import)
MUI — ~300KB (treeshakeable with proper imports)
AG Grid Community — ~400KB
TanStack Table — ~14KB headless
```
**Step 3: Maintenance status** (check npm download trends, GitHub pulse, last release)
- Avoid libraries with <6 months of no activity if they're in your critical path
- Radix UI, TanStack, shadcn/ui, MUI — all actively maintained as of 2026
- Tremor: v3 rewrote to shadcn model — check docs match installed version
**Step 4: Accessibility requirements**
- Radix UI / shadcn/ui — WAI-ARIA by default
- MUI — ARIA by default
- Recharts/Nivo — limited ARIA; add `role="img"` + `aria-label` on chart containers manually
- AG Grid Community — basic ARIA; enterprise has more
---
### When to Build Custom vs Use Library
**Build custom when**
- The component is a core differentiator (e.g., specialized domain-specific visualizer)
- Existing libraries require more overriding than building fresh would take
- The interaction pattern is unique to your domain
**Use library when**
- It's a solved problem (date picker, data table, tooltip)
- You need to ship in days, not weeks
- The component needs accessibility out of the box
---
### Mixing Libraries — What Works, What Doesn't
**Safe combinations**
```
shadcn/ui + Recharts — standard, no conflicts
shadcn/ui + TanStack Table — headless table + shadcn Table styles
MUI + MUI X Charts — same design token system, no conflicts
antd + antd Charts (G2) — same ecosystem
Chakra + Nivo — Chakra for layout, Nivo for charts
Tremor + TanStack Table — when Tremor's table isn't enough
```
**Problematic combinations**
```
MUI + antd — two CSS-in-JS runtimes, conflicting theme systems, bundle bloat
Chakra v2 + MUI — two component libraries with overlapping primitives, style conflicts
Two date pickers — always pick one and standardize
Framer Motion + React Spring — both do animation; pick one
```
**Rules for mixing**
1. One "base" design system per project (MUI, Chakra, shadcn, or antd — not two)
2. Specialized libraries (charts, tables, date pickers) can come from anywhere
3. Never import full libraries — always treeshake with named imports
4. Audit bundle size after adding each library (`npx bundlephobia` or webpack-bundle-analyzer)
---
### Performance for Data-Heavy Dashboards
**Virtualization** (render only visible rows)
```tsx
// TanStack Virtual — list virtualization
import { useVirtualizer } from '@tanstack/react-virtual'
const rowVirtualizer = useVirtualizer({
count: rows.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 40, // estimated row height
overscan: 5, // extra rows above/below viewport
})
```
**Chart performance**
- Recharts SVG: good up to ~1,000 points. Above that, downsample on server before sending.
- For real-time streaming (10+ updates/second), use Canvas-based renderer (Chart.js or D3 directly)
- Memoize chart `data` arrays: `useMemo` prevents re-render on parent state changes
**Table performance**
- Server-side pagination is always preferable over client-side for operational data
- Column filtering should debounce (300ms) before triggering API call
- Use `React.memo` on custom cell renderers
**General**
- Code-split by route — chart library ~150KB should only load on dashboard routes
- Avoid rendering hidden tabs — use lazy-loaded Tab panels (`{activeTab === 'charts' && <Charts />}`)
- Skeleton loaders prevent layout shift on async data
---
## 6. Component Composition Patterns
### Card -> Header + Content + Footer
Standard dashboard card pattern that works across all libraries:
```tsx
// shadcn/ui — canonical pattern
<Card className="h-full">
<CardHeader className="flex flex-row items-center justify-between pb-2">
<CardTitle className="text-sm font-medium text-muted-foreground">
Revenue Today
</CardTitle>
<Badge variant={isActive ? "default" : "secondary"}>
{isActive ? "ACTIVE" : "INACTIVE"}
</Badge>
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">$12,040</div>
<p className="text-xs text-muted-foreground mt-1">+12% vs yesterday</p>
<AreaChart data={trendData} className="mt-4 h-24" />
</CardContent>
<CardFooter className="pt-0">
<Button variant="ghost" size="sm" className="px-0 text-xs">
View breakdown ->
</Button>
</CardFooter>
</Card>
```
---
### Dashboard Grid Layouts
**CSS Grid (recommended)**
```css
/* 12-column responsive grid */
.dashboard-grid {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: 1rem;
}
/* KPI row: 4 cards across */
.kpi-card { grid-column: span 3; } /* 4 across on desktop */
@media (max-width: 1024px) { .kpi-card { grid-column: span 6; } } /* 2 across tablet */
@media (max-width: 640px) { .kpi-card { grid-column: span 12; } } /* 1 across mobile */
/* Wide chart: 8 col, sidebar: 4 col */
.chart-main { grid-column: span 8; }
.chart-sidebar { grid-column: span 4; }
```
**Tailwind CSS Grid (shadcn projects)**
```tsx
<div className="grid grid-cols-12 gap-4">
<div className="col-span-3 md:col-span-6 sm:col-span-12"><KPICard /></div>
<div className="col-span-3 md:col-span-6 sm:col-span-12"><KPICard /></div>
<div className="col-span-3 md:col-span-6 sm:col-span-12"><KPICard /></div>
<div className="col-span-3 md:col-span-6 sm:col-span-12"><KPICard /></div>
<div className="col-span-8 md:col-span-12"><MainChart /></div>
<div className="col-span-4 md:col-span-12"><SidePanel /></div>
</div>
```
---
### Responsive Breakpoints for Data Dashboards
| Breakpoint | Width | Dashboard behavior |
|-----------|-------|--------------------|
| `sm` | 640px | Stack all cards. Hide secondary columns in tables. |
| `md` | 768px | 2-column KPI grid. Charts full width. |
| `lg` | 1024px | 3 or 4 column KPI grid. Sidebar visible. |
| `xl` | 1280px | Full dashboard layout. All columns visible. |
| `2xl` | 1536px | Consider max-width container — tables become hard to scan at full 1536px width. |
**Practical rule**: Design for `lg` first (most users are on desktop), ensure usable at `md` (tablet), don't optimize heavily for `sm` unless field workers use phones.
---
### Collapsible Sections and Drill-Down Patterns
**Collapsible section (shadcn/ui Collapsible)**
```tsx
<Collapsible open={isOpen} onOpenChange={setIsOpen}>
<CollapsibleTrigger asChild>
<Button variant="ghost" className="w-full justify-between p-4">
<span className="font-semibold">Team Breakdown</span>
<ChevronDown className={`h-4 w-4 transition-transform ${isOpen ? "rotate-180" : ""}`} />
</Button>
</CollapsibleTrigger>
<CollapsibleContent>
<TeamTable data={teamData} />
</CollapsibleContent>
</Collapsible>
```
**Drill-down pattern (click row -> detail panel)**
```tsx
// Table row click -> Sheet (slide-over)
const [selectedRow, setSelectedRow] = useState(null)
<Table>
{rows.map(row => (
<TableRow
key={row.id}
className="cursor-pointer hover:bg-muted"
onClick={() => setSelectedRow(row)}
/>
))}
</Table>
<Sheet open={!!selectedRow} onOpenChange={() => setSelectedRow(null)}>
<SheetContent side="right" className="w-[540px]">
<SheetHeader>
<SheetTitle>Record #{selectedRow?.id}</SheetTitle>
</SheetHeader>
<DetailView record={selectedRow} />
</SheetContent>
</Sheet>
```
**Tab-based drill-down** (when detail has multiple sections)
```tsx
// Dialog with Tabs for multi-section detail
<Dialog>
<DialogContent className="max-w-2xl">
<DialogHeader><DialogTitle>Shift Report — Day Shift</DialogTitle></DialogHeader>
<Tabs defaultValue="output">
<TabsList>
<TabsTrigger value="output">Output</TabsTrigger>
<TabsTrigger value="resources">Resources</TabsTrigger>
<TabsTrigger value="equipment">Equipment</TabsTrigger>
</TabsList>
<TabsContent value="output"><OutputDetail /></TabsContent>
<TabsContent value="resources"><ResourceDetail /></TabsContent>
<TabsContent value="equipment"><EquipmentDetail /></TabsContent>
</Tabs>
</DialogContent>
</Dialog>
```
---
## Quick Reference: Stack Recommendations by Project Type
| Scenario | Recommended Stack |
|----------|-----------------|
| New React + Tailwind project | shadcn/ui + Recharts + TanStack Table |
| Needs fastest time to dashboard | Tremor (all-in-one) |
| Heavy admin/internal tool | antd + ProComponents |
| MUI already in project | MUI + MUI X Charts + DataGrid |
| Complex data grid (Excel-like) | AG Grid Enterprise |
| Marketing/investor demo | shadcn/ui + Magic UI/Aceternity accents |
| Need unique design identity | Chakra UI + Nivo |
| Maximum performance, huge data | TanStack Virtual + AG Grid + hand-rolled SVG |
references/ux-principles.md
# UX Design Principles for B2B SaaS Dashboards and Data-Heavy Applications
A reference for building dashboards that serve operational users — people who need to act on data quickly, not just admire it.
---
## Table of Contents
1. [Information Hierarchy & Visual Weight](#1-information-hierarchy--visual-weight)
- F-pattern and Z-pattern scanning
- Visual hierarchy techniques
- Information scent
- Progressive disclosure
2. [Cognitive Load & Decision Psychology](#2-cognitive-load--decision-psychology)
- Miller's Law
- Hick's Law
- Decision fatigue
- Gestalt principles
- Change blindness
3. [Dashboard-Specific Patterns](#3-dashboard-specific-patterns)
- KPI cards
- Data tables
- Charts — when each type works
- Filters and controls
- Empty, loading, and error states
- Responsive data layouts
4. [B2B SaaS UX Anti-Patterns](#4-b2b-saas-ux-anti-patterns)
5. [Summary-First, Details-on-Demand](#5-summary-first-details-on-demand)
6. [Quick Reference Checklists](#6-quick-reference-checklists)
---
## 1. Information Hierarchy & Visual Weight
### F-Pattern and Z-Pattern Scanning
Users do not read dashboards — they scan them. Eye-tracking studies (Nielsen Norman Group) show two dominant scan patterns depending on content density.
**F-Pattern** — triggered by text-heavy or table-heavy layouts:
- Users scan the top horizontal band fully
- Drop down and scan a second shorter horizontal band
- Scan the left edge vertically
- Implication: critical information belongs top-left; right-side content is frequently missed
**Z-Pattern** — triggered by sparse or visual-first layouts:
- Eyes travel left-to-right across the top
- Diagonal sweep down to the bottom-left
- Left-to-right across the bottom
- Implication: use for landing pages and summary cards, not dense tables
**Practical rule for dashboards**: Assume F-pattern on any page with more than two rows of content. Put the single most important metric top-left. Put navigation and secondary actions on the right where they can be found when needed but do not interrupt scanning.
---
### Visual Hierarchy Techniques
Visual weight is the sense that some elements "demand attention first." You have five levers:
| Lever | How to use it | Common mistake |
|-------|---------------|----------------|
| **Size** | Larger = more important. Scale KPI numbers (32-48px) vs labels (12-14px) | Scaling everything large — destroys the hierarchy |
| **Color** | Use one accent color for critical/actionable items. Reserve red for genuine alerts | Using 6+ colors — users can't build a mental model |
| **Contrast** | High contrast = foreground. Low contrast = secondary info | Using light gray on white for anything users need to read quickly |
| **Spacing** | White space groups related items and separates unrelated ones | Dense packing — makes everything feel equally important |
| **Typography weight** | Bold for values, regular/light for labels and supporting text | Bolding labels instead of values — inverts the hierarchy |
**The 3-second rule**: A new user should be able to identify the three most important things on a page within 3 seconds. If you cannot achieve this, the visual hierarchy is broken.
---
### Information Scent
Information scent is the user's ability to predict whether clicking something will lead to what they need. Poor information scent causes users to abandon exploration.
**Strong scent signals**:
- Labels that match the user's mental model vocabulary (e.g., "Server Response Time" not "Resource Utilization Metric")
- Numeric previews in navigation items ("Alerts (3)" not just "Alerts")
- Consistent iconography — once a user learns that a trend arrow means "click for detail," it works everywhere
- Breadcrumb trails that show where you are, not just where you came from
**Weak scent signals**:
- Generic labels ("Details", "More", "View")
- Unlabeled icons — acceptable only for universal metaphors (home, search, close)
- Collapsed sections with no preview of what's inside
- Identical-looking cards with different behaviors
**Practical test**: Ask a user who has never seen the page "what would happen if you clicked X?" If they cannot answer confidently, the scent is weak.
---
### Progressive Disclosure
Progressive disclosure is the practice of showing only the information needed for the current task, revealing more complexity on demand.
**Why it works**: It reduces cognitive load at initial page render while preserving access to full depth for power users.
**Three tiers of disclosure in dashboards**:
1. **Tier 1 — Always visible**: Summary metrics, status indicators, critical alerts. Should be readable in seconds without interaction.
2. **Tier 2 — One interaction away**: Expandable rows, tooltip details, filter panels, drill-down charts. Triggered by hover or click.
3. **Tier 3 — Navigation away**: Full record detail, historical data, audit logs. Requires navigating to a dedicated page.
**Implementation patterns**:
- Expandable table rows for inline detail without losing table context
- Hover tooltips for chart data points — show exact values on demand
- "Show more" controls that expand a truncated list (show 5, expand to 20)
- Detail drawers/slideovers that open without full page navigation
- Collapsible sections with clear visual state (chevron icon, not just color)
**Common mistake**: Using progressive disclosure to hide information that users need constantly. If users expand the same row every time they visit, that data belongs in Tier 1.
---
## 2. Cognitive Load & Decision Psychology
### Miller's Law
George Miller's 1956 paper established that working memory holds approximately 7 +/- 2 chunks of information at once. Exceeding this causes users to lose track, make errors, and feel frustrated.
**Chunks, not items**: A "chunk" is a meaningful unit. "32%" is one chunk. A table with 32 rows is 32 chunks. But a table with 32 rows grouped into 4 logical categories (8 rows each) is closer to 4 chunks — the grouping compresses the cognitive cost.
**Dashboard applications**:
- Limit KPI cards on a single view to 5-7 (never more than 9)
- Group related metrics visually — e.g., CPU, memory, and disk are one chunk ("server health")
- Paginate tables at 15-25 rows; do not show 200 rows by default
- Navigation menus with more than 7 top-level items need grouping or restructuring
---
### Hick's Law
Decision time increases logarithmically with the number of choices. Adding options always adds friction — even options the user will never choose.
**Formula implication**: Going from 2 to 4 choices roughly doubles decision time. Going from 4 to 8 doubles it again.
**Dashboard applications**:
- Default the most common date range (e.g., "Today" or "This month") — do not make users choose every time
- Limit filter dropdowns to the most useful values; put "Other" or "Custom" at the bottom
- Preset report configurations for the 3-4 most common use cases, with "Custom" as an escape hatch
- Action menus with more than 5 items should be restructured — separate primary actions (prominent) from secondary (overflow menu)
**The paradox of choice in B2B**: Enterprise users often want the ability to configure everything, but they act faster with smart defaults. The solution is progressive configuration — sensible defaults with accessible customization, not 40 settings on first use.
---
### Decision Fatigue in Data-Dense UIs
Decision fatigue occurs when users have made many small decisions throughout a session and their judgment quality degrades. In B2B dashboards, this manifests as:
- Skipping data validation that should be done
- Approving records without reviewing them
- Abandoning workflows partway through
- Making errors in bulk-edit operations
**Design responses**:
- Reduce micro-decisions: auto-populate defaults wherever reasonable, especially in forms
- Batch related approvals — show 5 pending items together rather than forcing 5 separate page navigations
- Surface exceptions, not everything — a dashboard that shows 200 green rows and 3 red ones should make the 3 red ones unmissable, not require scanning all 203
- Progress indicators in multi-step forms reduce anxiety and keep users oriented (reducing the cognitive burden of tracking "where am I?")
---
### Gestalt Principles in Dashboard Design
Gestalt principles describe how the human visual system creates order from complex stimuli. They are not aesthetic preferences — they are perceptual facts.
**Proximity**: Elements close together are perceived as related. Use consistent spacing to visually group metrics that belong together (e.g., day/night shift values for the same KPI). Inconsistent spacing creates false groupings.
**Similarity**: Elements that look alike are assumed to behave alike. If KPI cards all use the same card style, users expect them all to be clickable (or none of them). Mixing interactive and non-interactive elements with identical styling is a high-friction failure mode.
**Continuity**: The eye follows lines and curves. Use alignment to create visual flow — aligning left edges of cards in a column signals they are a list. Misalignment disrupts scanning.
**Closure**: Incomplete shapes are perceived as complete. Partial circles (progress rings), truncated bars, and cut-off charts work because users mentally complete them. Use this deliberately — a half-filled ring for "47% plan attainment" is instantly readable.
**Figure-ground**: Users separate foreground (content) from background (container). Poor contrast between figure and ground makes content disappear. This is the #1 reason dark-mode dashboards fail — designers often forget to check all chart colors against the dark background.
**Common Gestalt violations**:
- Cards with different padding but same border — proximity grouping breaks
- Using the same color for both a trend indicator (positive) and an action button (neutral) — similarity principle creates confusion
- Orphaned labels with too much space between label and value — users cannot tell what the label refers to
---
### Change Blindness and Attention Patterns
Change blindness is the failure to notice visual changes when attention is not directed at the changing element. It is a fundamental property of human vision, not a user deficiency.
**Dashboard implications**:
- Users will miss data that refreshes silently in a corner. A loading spinner or brief "Updated 2s ago" indicator directs attention.
- If a KPI changes significantly between page loads, animate the number briefly or show a delta badge — passive updates are invisible
- Do not use color alone to indicate state changes (accessibility + change blindness combined risk). Use color + icon + text.
- Status badges that blink or pulse for 2-3 seconds after a value change are not gimmicks — they exploit pre-attentive processing to overcome change blindness
**Pre-attentive attributes** (processed before conscious attention, extremely fast):
- Color — red cells in a green table are detected in ~50ms
- Size — a larger number stands out immediately
- Motion — animation in a static UI draws the eye instantly (use sparingly for precisely this reason)
- Orientation — a diagonal element in a grid of horizontal elements is immediately salient
Use pre-attentive attributes to direct attention to exceptions and critical states. Avoid using them for decoration.
---
## 3. Dashboard-Specific Patterns
### KPI Cards — What Works, What Doesn't
KPI cards are the highest-visibility real estate on any dashboard. They communicate "is the business healthy right now?"
**Anatomy of an effective KPI card**:
```
[Icon/Category label — small, low contrast]
[Primary value — large, high contrast, bold]
[Unit or context — small, secondary]
[Trend indicator — delta vs. previous period, colored]
[Sparkline — optional, shows trend shape]
```
**What works**:
- Single value per card — never two competing numbers at equal size
- Contextual delta: "+12% vs yesterday" beats a standalone number
- Consistent card sizes — variable sizes imply variable importance; use it deliberately
- Color that carries meaning: green/amber/red for status, not decoration
- Click-through to the underlying data — every KPI card should link to the drill-down view
**What doesn't work**:
- Percentage without absolute value: "+23%" is useless without "from 400 to 492"
- Stale data without timestamp: users cannot trust a KPI if they do not know when it was last updated
- More than 2 decimal places on operational metrics: "1,234.56" is precise enough; "1,234.5678" is noise
- Tooltips as the primary explanation: if a user needs a tooltip to understand what a KPI measures, the label is wrong
- Treating all KPIs as equal — the hero metric should be visually dominant
**Common mistakes**:
- Cards with no hierarchy — all 8 metrics at the same size and weight
- Showing MTD numbers without showing the target — context-free metrics are unactionable
- Loading states that shift layout — always use skeleton screens, never show empty space then pop content in
---
### Data Tables — When to Use, Column Prioritization, Row Density
Tables are the right choice for:
- Comparing multiple attributes across many items (equipment list, daily records)
- Enabling user-driven sorting and filtering
- Showing exact values that users need to read precisely
- Supporting bulk selection and batch operations
Tables are the wrong choice for:
- Showing a single metric over time (use a line chart)
- Showing proportional distribution (use a bar or pie chart)
- Summarizing totals for non-technical users (use KPI cards)
**Column prioritization rules**:
1. Most important columns leftmost (where F-pattern scanning begins)
2. Numeric columns right-aligned — this aligns decimal places for comparison
3. Text columns left-aligned
4. Dates: relative ("3 days ago") for recent; absolute for historical
5. Status columns: use color + text, never color alone
6. Action columns rightmost — separated from data columns visually
7. Maximum 6-8 columns visible without horizontal scroll; hide the rest behind a column picker
**Row density options** (offer all three in B2B products):
- Compact (24-28px row height) — power users processing large datasets
- Default (36-40px) — standard operational use
- Comfortable (48-56px) — scanning or touchscreen use
**Table UX checklist**:
- Sticky column headers on scroll
- Sortable columns — clicking header sorts; clicking again reverses; visual indicator of current sort
- Pagination OR infinite scroll (not both) — infinite scroll works poorly with filters
- Row hover state for clickable rows
- Selected row state for checked rows
- Empty state message when filters return no results (not a blank table)
- Loading skeleton that matches the table structure, not a spinner in the middle of the page
---
### Charts — When Each Type Works
Choosing the wrong chart type undermines trust in the data. Match the chart to the question being answered.
**Line chart** — best for: continuous data over time, trends, rate of change
- Use when: daily metrics over a month, resource consumption over a week
- Works poorly when: comparing discrete categories, showing composition
- Rules: start y-axis at 0 for absolute values; may start at a non-zero baseline for rates/percentages if clearly labeled; max 3-4 lines before it becomes unreadable; use different line styles not just colors for accessibility
**Bar chart (vertical)** — best for: comparing discrete categories, ranking
- Use when: output by team this month, usage by product
- Works poorly when: more than ~10 categories (switch to horizontal bar), time series with many points
- Rules: sort bars by value (not alphabetically) unless the category order has inherent meaning; include value labels on bars for B2B users who need exact numbers
**Horizontal bar chart** — best for: long category labels, ranking with many items
- Use when: equipment list sorted by usage, products sorted by revenue
- Works better than vertical when: category names are more than 3-4 words
**Area chart** — best for: cumulative totals over time, showing volume
- Use when: cumulative output vs target (the fill emphasizes progress toward a goal)
- Works poorly when: multiple overlapping areas (use line chart instead)
**Sparkline** — best for: trend direction at a glance inside a table or KPI card
- Use when: "is this metric going up or down this week?" — not for reading exact values
- Works poorly when: users need to compare sparklines across rows (scale varies)
**Stacked bar** — best for: part-to-whole relationships over categories
- Use when: showing how total output is split by category per day
- Works poorly when: more than 4-5 segments (color confusion) or when the middle segments are important (only bottom and top are easy to read)
**Heatmap** — best for: two-dimensional patterns, time-of-day x day-of-week, anomaly detection
- Use when: resource consumption by hour and day (reveals idle-time patterns), attendance patterns
- Works poorly when: users need exact values; heatmaps are for pattern recognition, not precision
**Pie / donut chart** — best for: composition with 3-4 segments, not for ranking
- Avoid when: more than 4 segments, when users need to compare non-adjacent slices, when absolute values matter
- The donut variant (with a center value) is better than pie because it shows the total
- In B2B dashboards: use sparingly. Bar charts are almost always more readable for the same data.
---
### Filters and Controls — Placement, Progressive Disclosure
Filters are the navigation system for data. Their placement and behavior determine whether users can find the slices they need.
**Placement patterns**:
- **Horizontal filter bar** (top of page) — best for 3-5 key filters; scannable; does not eat vertical space
- **Sidebar filter panel** — best for 6+ filters; supports faceted filtering (like e-commerce); hides complexity without losing access
- **Inline filters** (inside tables/charts) — best for column-specific controls; keeps context
- **Global filters** (persistent across views) — for tenant/site/period selectors that affect all data on the page
**Progressive disclosure for filters**:
1. Show 3-5 most-used filters always visible
2. "More filters" expansion reveals advanced options (date range, category selector)
3. Active filter chips show what is currently applied — always include a "Clear all" action
4. Saving filter presets ("Last month, Team A only") prevents repeated configuration for power users
**Filter UX rules**:
- Apply filters immediately on selection (do not require a "Submit" button for filters)
- Show result count update in real time as filters change ("Showing 47 of 312 records")
- Persist filters on page reload unless the user explicitly clears them
- Date range pickers: always offer presets (Today, This week, This month, Last month, Custom) — never raw date-only input as the primary option
- Multi-select dropdowns: show selected count in the trigger ("Product type: 3 selected")
---
### Empty States, Loading States, Error States
These three states are treated as afterthoughts in most B2B products. They are the moments users form lasting impressions about product quality.
**Empty states** (no data to show):
- Distinguish between "no data exists yet" vs "filters returned no results"
- "No data yet" empty state: explain what will appear here, provide a call to action to add data
- "No results" empty state: show active filters, suggest broadening the search, never just a blank table
- Never: a fully white page with nothing on it
- Example: empty records table -> "No records for this period. [Change date range] or [Add record]"
**Loading states**:
- Skeleton screens (gray placeholder shapes matching the content layout) are 40% faster perceived than spinners (Viget research, 2016)
- Use spinners only for actions, not for page loads
- Show partial data as it loads — do not wait for all data before rendering anything
- If loading takes more than 3 seconds, show a message explaining why (e.g., "Calculating monthly aggregates...")
- Never let a loading spinner run forever — set a timeout and show an error state
**Error states**:
- Be specific: "Could not load usage data for January. The database query timed out." beats "Something went wrong."
- Provide a recovery action: [Retry], [Refresh page], [Contact support]
- Never hide errors behind a console — surface them in the UI
- For form submission errors: highlight the specific fields that failed, not just a toast at the top
- For partial failures (some data loaded, some did not): show what loaded and mark the failed sections clearly
---
### Responsive Data Layouts
B2B dashboards are primarily desktop applications, but users often check data on phones. Responsive design for data-heavy UIs requires deliberate choices, not just fluid grids.
**Desktop-first for dashboards**: Unlike consumer apps, B2B dashboards should be designed for desktop first. The data density required by operational users does not compress well to mobile without deliberate redesign.
**Breakpoint strategies for data tables**:
- At tablet width: hide lower-priority columns, increase touch targets
- At mobile width: switch to card-based layout (one card per row, showing 3-4 key fields) — do not force a wide table onto a narrow screen
- Provide a "simplified view" toggle for mobile-constrained users
**KPI card grids**: Use CSS Grid with `auto-fill` and `minmax()` — cards reflow naturally. Set a minimum card width that ensures readability (240px minimum).
**Navigation**: On mobile, collapse the sidebar to a bottom tab bar or hamburger. In B2B contexts, limit mobile navigation to the 4-5 most-used sections.
---
## 4. B2B SaaS UX Anti-Patterns
These patterns are extremely common in enterprise and B2B products. They persist because they feel "organized" during design but create friction during use.
### Tab Overuse — Hiding Important Information Behind Clicks
**The problem**: Tabs fragment related data into separate views, forcing users to mentally integrate information across navigation interactions. A user comparing metrics across two categories must remember numbers from Tab A while looking at Tab B.
**Where tabs are appropriate**: Switching between fundamentally different modes or contexts (Settings vs Data Entry vs Reports). Tabs are not appropriate for related data that users need to compare.
**Better alternatives**:
- Put related data on the same page with clear section headers
- Use expandable/collapsible sections to manage density
- Use a split view or side-by-side layout for comparison data
- Consolidate the "Details", "History", and "Audit" tabs on a record page into a single scrollable page with anchor links
### Dashboard Bloat — Too Many Metrics
**The problem**: When everything is a KPI, nothing is. Product teams add metrics because stakeholders request them. The result is a 20-card dashboard where the user cannot identify the 3 things that actually need attention today.
**Signs of dashboard bloat**:
- More than 9 KPI cards on a single view
- Metrics that have never shown a non-green status
- Metrics that no one in the organization is responsible for acting on
- Multiple metrics measuring the same underlying thing at different granularities, all shown simultaneously
**The cure**:
- For each metric: "If this goes red, who acts, and what do they do?" If the answer is unclear, it should not be on the main dashboard.
- Role-based dashboards — the manager sees efficiency KPIs; the data entry operator sees today's entry status
- Move vanity metrics to a secondary "Reports" section
### Equal Visual Weight for Unequal Importance
**The problem**: When all elements on a page look identical (same card size, same font weight, same color), the user must read everything before they can identify what matters. This is the visual equivalent of a document with no headings.
**Examples**:
- 8 KPI cards all the same size — one card is "System Uptime" (critical: downtime means operations stop today), another is "Average Session Duration" (not actionable)
- A table with 12 columns all at equal width — the "Date" column is given the same space as the "Total Revenue" column
- Action buttons and informational labels using the same visual style
**The cure**: Map every element to a tier (critical / important / supporting) and establish a visual vocabulary that consistently represents each tier. Apply it without exception.
### Context Switching Between Pages for Related Data
**The problem**: Users must navigate between two or more pages to complete a single mental task. Every navigation is a context switch — the user must reload their mental model of where they are and what they were doing.
**Common examples**:
- "Orders" and "Inventory" are separate pages, but inventory balance is directly affected by order entries — users must navigate back and forth to reconcile
- Creating a record on Page A requires looking up an ID from Page B
- Viewing a KPI and seeing the underlying records requires navigating to a different module
**The cure**:
- Inline lookups: show related data as a tooltip, drawer, or expandable section — avoid full navigation for read-only reference data
- Contextual previews: hovering a record reference shows a summary popup
- Guided flows: when a user creates an entry, show the current balance inline in the form
- Co-locate data that is operationally related, even if it comes from different backend modules
### Over-Relying on Tables When Visual Summaries Would Work Better
**The problem**: Tables are the path of least resistance for developers (just render the database query). But tables require users to build the mental picture themselves — reading numbers, doing subtraction in their head, finding the outlier in 50 rows.
**When a table is the wrong tool**:
- Showing trends over time (use line chart)
- Comparing proportions (use bar chart)
- Identifying outliers in a dataset (use sorted bar chart with color thresholds)
- Showing progress toward a target (use progress bar, gauge, or bullet chart)
**The rule**: Before using a table, ask "what question is the user trying to answer?" If the answer involves a comparison, trend, or proportion, start with a chart. Tables are for lookup ("show me the exact value for item X on day Y") not analysis ("which item has the highest output this month?").
---
## 5. Summary-First, Details-on-Demand
### Ben Shneiderman's Mantra
Ben Shneiderman formulated this principle in 1996 for information visualization:
> **Overview first, zoom and filter, then details on demand.**
This is not a suggestion — it is the foundational principle of all usable data-dense interfaces. Every well-designed dashboard implements this in layers:
1. **Overview**: The aggregate, the summary, the answer to "is everything okay?" — visible without any interaction
2. **Zoom and filter**: The ability to narrow scope — by time, category, entity — to isolate the area of interest
3. **Details on demand**: The full record, the raw data, the audit log — available on request without cluttering the overview
### How to Implement This in Dashboard Design
**Layer 1 — Overview (0 interactions)**:
- KPI cards with trend indicators
- Status summary badges (e.g., "3 alerts", "2 pending approvals")
- Aggregate charts showing the current period at a glance
- Should answer: "Is everything on track? Is anything critical broken?"
**Layer 2 — Zoom and filter (1-2 interactions)**:
- Filter panel to narrow date range, category, or entity
- Clicking a KPI card to drill into its component metrics
- Expanding a chart to show a longer time window
- Should answer: "Which sub-area is underperforming? When did it start?"
**Layer 3 — Details on demand (3+ interactions or dedicated navigation)**:
- Full data table with all columns
- Individual record detail pages
- Audit logs and change history
- Raw export (CSV/Excel)
- Should answer: "What was the exact value on day X? Who entered it?"
**The principle in navigation architecture**:
- Dashboard -> Module List -> Module Detail -> Record Detail -> Audit History
- Each level is one step deeper; users never need to go deeper than their question requires
- Breadcrumbs or back navigation make it easy to return to the previous level
---
### Examples from Successful B2B Products
**Stripe Dashboard**:
- Overview: Revenue this period, Payment success rate, Dispute rate — all above the fold
- Zoom: Click any metric to see daily breakdown chart
- Details: Click any day to see transactions, click any transaction for full record
- The default view never shows individual transactions — they are two clicks away
**Linear (project management)**:
- Overview: Issue count by status (To Do / In Progress / Done) as summary cards
- Zoom: Click a status to see the filtered issue list
- Details: Click an issue for the full record with comments, history, sub-issues
- Keyboard shortcuts let power users access detail quickly without mouse navigation
**Datadog (monitoring)**:
- Overview: Service health as colored tiles (green/yellow/red) — all services at a glance
- Zoom: Click a service to see its metrics dashboard — time-series charts pre-filtered to that service
- Details: Click a time range on a chart to expand it; click a data point to see the exact value and correlated events
- Alert severity is visually encoded (color + icon) — critical issues are unmissable
**Grafana (observability)**:
- Panels (KPI cards + charts) compose a dashboard — each panel is independently zoomable
- Variable filters at the top affect all panels simultaneously — one interaction changes the entire page's data
- "Explore" mode is a dedicated deep-dive environment — separate from the overview dashboards
- Time range control is global and persistent — users never have to re-set the time range for each chart
**Common thread**: All four products implement Shneiderman's mantra as a literal navigation hierarchy. The overview is never a scaled-down version of the detail view — it is a qualitatively different representation of the same data designed for a different question.
---
## 6. Quick Reference Checklists
### Dashboard Design Review Checklist
**Hierarchy**
- [ ] Can a new user identify the top 3 metrics within 3 seconds?
- [ ] Is the most important metric top-left or visually dominant?
- [ ] Are interactive and non-interactive elements visually distinct?
**Cognitive load**
- [ ] Fewer than 9 KPI cards on a single view
- [ ] Tables paginated at 20-25 rows
- [ ] Filters have smart defaults (not blank on load)
- [ ] No more than 5 top-level navigation items without grouping
**Data visualization**
- [ ] Each chart answers a specific question (documented or obvious from label)
- [ ] Axes labeled with units
- [ ] Color used consistently (green = good, red = bad, blue = neutral/actionable)
- [ ] Color not the sole indicator of status (icon or text also present)
**States**
- [ ] Empty state for every list/table (explains what will appear and how to add it)
- [ ] Loading skeleton matches content layout
- [ ] Error state includes a recovery action
**Progressive disclosure**
- [ ] Overview answers "is everything okay?" without interaction
- [ ] Detail is available within 2-3 clicks from overview
- [ ] Dense data (full tables, audit logs) is behind dedicated navigation
### KPI Card Quick Check
- [ ] One primary value per card
- [ ] Delta vs previous period shown
- [ ] Timestamp of last update shown (or implicit from page-level refresh indicator)
- [ ] Card is clickable and leads to drill-down
- [ ] Units are explicit (MT, L, %, $)
### Table Quick Check
- [ ] Most important columns leftmost
- [ ] Numeric columns right-aligned
- [ ] Column count <= 8 visible by default
- [ ] Sortable headers with visual sort indicator
- [ ] Sticky header on scroll
- [ ] Pagination controls visible
### Form / Data Entry Quick Check
- [ ] Submit button uses `disabled` + visual loading state during submission
- [ ] Validation errors shown inline, not only in a toast
- [ ] Contextual reference data shown inline (e.g., current balance in a form)
- [ ] Defaults pre-populated where unambiguous (current date, current period)
---
*Reference compiled from: Nielsen Norman Group research, Shneiderman (1996) "The Eyes Have It", Fitts (1954), Miller (1956), Hick (1952), Gestalt psychology literature, and UX analysis of Stripe, Linear, Datadog, and Grafana dashboards.*
SKILL.md
---
name: ux-expert
description: 'UX design expert for auditing and redesigning pages, dashboards, and data-dense interfaces via 4-phase collaborative reviews across 8 UX dimensions. Triggers on: "review UX", "audit this page", "redesign the dashboard", "UX review", "improve the layout", "dashboard UX audit", "component recommendations".'
metadata:
version: 1.0.0
category: review
tags: [ux, dashboard, audit, redesign, b2b-saas]
difficulty: intermediate
phase: review
---
# UX Expert
You are a seasoned UX professional with 15+ years designing B2B SaaS dashboards, analytics tools, and data-heavy applications. You've designed products at the level of Stripe, Linear, and Datadog. You think in terms of information architecture, cognitive load, and user psychology — not just aesthetics.
Your superpower: you can look at a page and immediately identify why it feels "off" — the hierarchy is flat, the eye has nowhere to land, the data is organized by implementation convenience rather than user mental model, or the interaction cost is too high for the value delivered.
## How You Work
You are **collaborative, not prescriptive**. You explain your reasoning at every step so the user understands WHY you're making each decision. You present options, explain trade-offs, and ask for the user's input before finalizing. The user should feel like they're learning UX principles through the process, not just receiving instructions.
### Workflow Phases
**Phase 1: Understand** (always do this first)
1. Read the actual components — understand what data is available, what the current layout is, how state flows
2. Identify the user's primary tasks on this page — what are they trying to accomplish?
3. Understand the tech stack — what libraries are already in use? What's the design system?
**Phase 2: Audit** (present findings conversationally)
1. Walk through each of the 8 UX dimensions (see `references/audit-methodology.md`)
2. For each finding, explain the problem AND the UX principle behind it
3. Rate severity: Critical > Major > Minor > Enhancement
4. Present findings grouped by impact, not by dimension — lead with the biggest wins
**Phase 3: Propose** (collaborative redesign)
1. Present 1-2 layout concepts as ASCII wireframes
2. Explain the rationale for each major decision
3. Ask the user which direction resonates
4. Iterate based on feedback
5. Recommend specific components from the project's existing library or suggest new ones (see `references/component-libraries.md`)
**Phase 4: Spec** (actionable output)
1. Produce a detailed redesign spec with:
- ASCII wireframe of the final layout
- Component inventory (what to use, from which library)
- Data requirements (what API data feeds each section)
- Interaction specifications (hover, click, expand, filter)
- Responsive behavior (breakpoints, what collapses)
2. The spec should be detailed enough that a developer can implement it without asking clarifying questions
## Reference Files
Read these as needed — don't load all upfront:
- **`references/ux-principles.md`** — Core UX theory: visual hierarchy, cognitive load, Gestalt principles, dashboard patterns, anti-patterns, Shneiderman's mantra. Read this when you need to cite a principle or need inspiration for a pattern.
- **`references/audit-methodology.md`** — The 8-dimension audit framework, severity ratings, code reading guide, finding template, redesign spec template. Read this when starting an audit.
- **`references/component-libraries.md`** — Modern UI libraries (antd, shadcn, Recharts, Tremor, 21st.dev, etc.), when to use each, decision framework, composition patterns. Read this when recommending components.
## Key Principles to Always Apply
### 1. Summary First, Details on Demand
Ben Shneiderman's mantra: "Overview first, zoom and filter, then details on demand." Every dashboard page should have a scannable summary layer that answers "how are things going?" in 3 seconds. Details come on interaction (expand, click, drill-down), not by default.
### 2. Visual Hierarchy = Priority Hierarchy
If everything is bold, nothing is bold. The most important metric should be the largest, highest-contrast element on the page. Secondary metrics should be visually subordinate. Tertiary details should require interaction to see.
### 3. Group by User Mental Model, Not by Data Source
Users don't think in API endpoints or database tables. They think in tasks and questions: "How is my team doing?" "Is resource consumption normal?" "What needs attention?" Group information by the question it answers, not by where it comes from.
### 4. Reduce Interaction Cost for Primary Tasks
If the user needs 3 clicks and a tab switch to answer their most common question, the page has failed. The most frequent task should require zero clicks (visible on load). The second most frequent task: one click. Everything else: deeper.
### 5. Tabs Are Usually the Wrong Answer
Tabs hide information behind clicks, create equal visual weight for unequal importance, and prevent comparison across categories. Before using tabs, ask: "Could this be a scrollable single page with sections?" Usually yes.
### 6. Screen Real Estate Is Precious
Every pixel should earn its place. If a section takes 25% of the viewport but delivers 5% of the value, it needs to shrink or be moved to a drill-down. Common offenders: oversized headers, padded-out cards with one number, charts with too much whitespace.
### 7. Context-Aware Color
Color should encode meaning, not decoration. Green = good, red = bad, amber = warning. But "good" depends on the metric: revenue up = green, costs up = red. Never use color purely for visual variety.
## Communication Style
- Lead with the finding and why it matters, then cite the principle
- Use plain language — "the eye has nowhere to land" not "the visual hierarchy lacks a dominant focal point"
- Show before/after when possible (ASCII wireframes)
- When presenting options, state your recommendation and why, but let the user decide
- If you're uncertain about a domain-specific decision (e.g., which metric matters most to users), ask rather than guess
## Example Interaction Flow
```
User: /ux-expert audit the analytics page
You: Let me read the components first...
[reads Analytics.tsx, tabs, API client]
"I've reviewed the Analytics page. Here's what I found, biggest impact first:
**1. Tab overuse hiding insights (Major)**
All 4 analytics views are behind tabs — the user sees only 1/4 of their data at any time.
This violates Shneiderman's mantra: no overview exists. A manager opening this page
can't scan "how are things going?" without clicking through each tab.
**Recommendation:** Single scrollable page with a summary strip at top (4 key metrics),
then expandable sections for each area. Here's a rough layout:
┌──────────────────────────────────────────────────┐
│ [Workspace ▾] [Feb 2026 ▾] │
├──────────────────────────────────────────────────┤
│ Revenue $1.2M↑ │ Utilization 62% │ Cost ↓ │ ← Summary strip
├──────────────────────────────────────────────────┤
│ ▼ Team Performance │ ← Expanded by default
│ [ranked tiles] [trend chart] │
├──────────────────────────────────────────────────┤
│ ► Resource Utilization │ ← Collapsed
│ ► Cost Analysis │
│ ► Anomalies (2 flagged) │ ← Badge draws attention
└──────────────────────────────────────────────────┘
What do you think about this direction?"
User: I like the summary strip idea but I want all sections visible
You: "Got it — all sections visible, no collapsing. That works well if we make the sections
compact. Let me revise..."
```
## What This Skill Does NOT Do
- Write production code (it produces specs for developers/Claude to implement)
- Make decisions about business logic (which metric matters most is a product/domain question — it will ask)
- Override the existing design system — it works within what's already there, suggesting additions only when justified
- Auto-trigger — only runs when explicitly invoked with `/ux-expert`