| name | dashboard-design |
| description | Design patterns for data-dense dashboard UIs with dark themes. Use when building tables, charts, status panels, and data visualization components. Focuses on readability, information density, and consistent design systems. |
Dashboard Design Patterns
Focused design guidance for data-dense dashboard UIs — trading dashboards, analytics panels, code review tools, monitoring interfaces. Complements general frontend design skills with patterns specific to tabular data, charts, status indicators, and dark-theme-first development.
Data Tables
Numeric Formatting
- Use
font-variant-numeric: tabular-nums on ALL number columns — prices, quantities, percentages, scores. Without this, columns jitter as values change.
- Right-align numeric columns, left-align text columns. This is not optional — it's how humans scan numbers.
- Format consistently: if one price shows
$1,234.56, all prices show two decimal places. Don't mix $5 and $5.00.
Column Layout
- Use
table-layout: fixed with <colgroup> for consistent column widths. Auto-layout causes content-dependent jitter.
- Define minimum column widths so narrow data doesn't collapse columns to unreadable sizes.
- Consider sticky first column (symbol/name) on horizontally scrollable tables.
Row Differentiation
- Use ONE technique: subtle alternating backgrounds OR increased row spacing — not both. Both together creates visual noise.
- Alternating backgrounds: keep contrast extremely subtle (2-3% lightness difference). The goal is a guide rail for the eye, not a zebra.
- Add a hover highlight row to help track across wide tables.
- Every row looking identical is a hierarchy problem — use font-weight on the primary column (bold for Symbol, normal for secondary data).
Sortable Columns
- Active sort column needs a clear indicator (arrow icon + subtle column highlight).
- Inactive sortable columns: show a muted indicator on hover only.
- Multi-sort: show sort priority numbers if supported.
Empty & Missing Data
- Empty cells: use em-dash (—) styled muted (
color: var(--color-text-tertiary)) — never leave cells blank (blank looks like a rendering bug).
- When MOST cells in a column are empty, that's a data/design problem: consider hiding the column, showing a summary row, or collapsing to a denser layout.
- Distinguish between "no value" (—), "zero" (0), and "not applicable" (n/a).
Color System for Dashboards
Semantic Color Tokens
Define strict semantic tokens and enforce their usage:
:root {
--color-positive: ;
--color-negative: ;
--color-neutral: ;
--color-warning: ;
--color-info: ;
--color-accent: ;
--color-text-primary: ;
--color-text-secondary: ;
--color-text-tertiary: ;
}
Color Discipline
- NEVER reuse positive/negative colors for unrelated status. Green for "profit" AND "enabled" AND "fresh" AND "connected" is confusing. Use:
--color-positive / --color-negative: ONLY for financial direction (P&L, price change, delta)
--color-accent: for active/enabled/selected states
--color-info: for informational status, connectivity, freshness
- For P&L specifically: green for positive, red for negative, neutral for zero — no exceptions, no other uses of those exact shades.
- Use colorblind-safe palettes: always supplement color with icons, position, or text labels (up/down arrows alongside green/red).
Surface Elevation (Dark Themes)
Define 2-3 elevation levels with subtle lightness steps:
:root {
--surface-0: hsl(220 20% 8%);
--surface-1: hsl(220 18% 11%);
--surface-2: hsl(220 16% 14%);
--surface-3: hsl(220 14% 18%);
}
- Use lightness steps of 2-4% between levels — subtle enough to not look striped, clear enough to establish depth.
- Tint all neutrals toward your primary hue (e.g., toward blue/slate for a trading dashboard). Pure gray causes "gray fatigue" in data-dense interfaces.
Typography for Data
Font Choices by Context
- Monospace or tabular-nums: prices, quantities, percentages, timestamps, IDs, hashes
- Proportional (body font): names, descriptions, labels, prose
Hierarchy in Table Cells
- Bold (600) for primary identification column (Symbol, Filename, ID)
- Normal (400) for secondary data columns
- Muted color + lighter weight for tertiary/metadata
Label + Value Pattern
For key-value displays (detail panels, summary cards):
Muted small label
PROMINENT VALUE
The label is --color-text-tertiary at smaller size; the value is --color-text-primary at larger/bolder size.
Size Minimums
- Data in tables: minimum 13px (0.8125rem)
- Secondary labels/captions: 11px absolute minimum (0.6875rem)
- Line-height: 1.2-1.3 for dense tables, 1.4-1.5 for prose/descriptions
- Increase line-height by 0.05-0.1 in dark mode (light text on dark renders thinner)
Status & State Indicators
Badge Design
- Badges must have distinct shapes/borders, not JUST color — accessibility requires multiple visual channels.
- Pattern: colored dot + text label, or pill with tinted background + darker text of the same hue.
- Use semantic names in code:
status="success", status="warning" — never color="green", color="yellow".
Active/Enabled States
- Use
--color-accent (your primary/brand color) for active, enabled, selected states — NOT green.
- Green is reserved for positive financial direction. "Enabled" is not "positive."
Stale/Aging Indicators
- Reduce opacity AND change text color — don't change just one. A single change is too subtle.
- Consider showing elapsed time: "Updated 2h ago" with progressively muted styling.
Scores & Ratings
- Don't show just a bare number. Add context: a subtle background bar, a small sparkline, or at minimum a range indicator.
- Conditional formatting: tint the cell background based on value range (heatmap-style) at very low opacity.
Empty States
Be Specific, Not Lazy
- "No data yet" is lazy. Tell the user WHEN to expect data: "Signals appear after the 8:00 ET analysis run" or "Trades will show here once your first order executes."
- Include an action when possible: "No watchlist items. Search for a symbol to add one."
Visual Structure
- Empty tables: show 2-3 ghost/skeleton rows to establish the table's visual structure. An empty white box communicates nothing about what will eventually appear.
- Maintain column headers even when empty — they orient the user.
Distinguish Empty Reasons
Four completely different states that all look like "nothing here":
- No data exists — "No signals yet" + explanation of when they'll appear
- Loading — skeleton rows, shimmer animation
- Error — error message with retry action
- Filtered to nothing — "No results match your filters" + clear-filters button
Never conflate these. Each needs distinct visual treatment and copy.
CSS Modules Conventions
Token-First Architecture
- Use CSS custom properties for ALL colors, spacing, and typography values — never hardcode hex/rgb/hsl values directly.
- Define tokens at
:root level, reference everywhere via var(--token).
- Even for "one-off" colors, create a token. Future-proofs theming and prevents orphan values.
Component-Scoped Overrides
.panel {
--panel-padding: var(--space-md);
--panel-bg: var(--surface-1);
padding: var(--panel-padding);
background: var(--panel-bg);
}
.panel.compact {
--panel-padding: var(--space-sm);
}
Naming Conventions
- Semantic token names:
--color-text-primary, --color-surface-1, --space-md
- NOT value-based:
--color-gray-700, --spacing-16
- Component tokens can reference global tokens:
--table-header-bg: var(--surface-2)
Dark Theme Specifics
Foundation
- Set
color-scheme: dark on the <html> element — this tells the browser to render scrollbars, form controls, etc. in dark mode.
- Never use pure black (#000) backgrounds. Use a very dark tinted color:
hsl(220 20% 8%) or similar.
- Page background should be the darkest surface. Cards/panels are slightly lighter. Popovers/modals lighter still.
Typography Adjustments
- Reduce font weight by one step in dark mode: 500 → 400 for body, 700 → 600 for bold. Light text on dark backgrounds renders perceptually thicker due to irradiation.
- Slightly increase line-height (+0.05-0.1) for the same reason.
Borders & Dividers
- Use semi-transparent white:
rgba(255, 255, 255, 0.08) to rgba(255, 255, 255, 0.12) — not opaque dark gray.
- Semi-transparent borders adapt automatically when surface colors change (elevation, hover states, etc.).
- Table cell borders: very subtle (
rgba(255, 255, 255, 0.06)) — just enough to separate, not enough to create a grid.
Accent Colors
- Desaturate slightly compared to light mode — high saturation on dark backgrounds is visually aggressive.
- Test accent colors against ALL surface levels, not just the base background.
Slide-Out Panels
Dimensions
- Width: 400-560px for detail views. Never wider than 40% of viewport.
- On narrow viewports (<900px), go full-width instead of a narrow sliver.
Behavior
- Backdrop: semi-transparent overlay. Click backdrop to close.
- ESC to close. Trap focus inside the panel while open.
- Content scrolls independently from main content (the panel body scrolls, not the page).
- Header stays fixed with close button, always visible.
Transitions
- Slide in from the right: 250-300ms, ease-out-quart.
- Backdrop fades in simultaneously: 200ms opacity transition.
- Exit is faster: ~200ms.
Charts
Color Usage
- 2-3 colors maximum per chart. If you need more, your chart is trying to show too much — split into multiple charts.
- Ensure line/bar colors pass 3:1 contrast against the chart background.
- Use the same semantic colors as the rest of the UI:
--color-positive for gains, --color-negative for losses.
Axis Formatting
- Abbreviated numbers: $1.5B not $1,500,000,000. 1.2K not 1,200.
- Sensible tick intervals: don't label every data point. Let the charting library auto-calculate, but verify it's not too sparse or dense.
- Include a zero baseline/reference line when showing P&L or relative changes.
- Y-axis labels: right-align for left-axis, left-align for right-axis.
Legends & Labels
- If only 1-2 series: label directly on the chart (inline labels), skip the legend.
- For comparison charts: use consistent line styles (solid for primary, dashed for benchmark/comparison).
- Crosshair/tooltip on hover showing exact values — essential for data-dense charts.
Dark Theme Charts
- Chart background should match or be slightly darker than the card surface.
- Grid lines: very subtle (
rgba(255, 255, 255, 0.05)).
- Reduce line thickness slightly — lines appear thicker on dark backgrounds.
Quick Reference: CSS Custom Property Naming
:root {
--color-text-primary: ...;
--color-text-secondary: ...;
--color-text-tertiary: ...;
--color-positive: ...;
--color-negative: ...;
--color-warning: ...;
--color-info: ...;
--color-accent: ...;
--surface-0: ...;
--surface-1: ...;
--surface-2: ...;
--space-xs: 4px;
--space-sm: 8px;
--space-md: 16px;
--space-lg: 24px;
--space-xl: 32px;
--space-2xl: 48px;
--font-body: ...;
--font-mono: ...;
--text-xs: 0.6875rem;
--text-sm: 0.8125rem;
--text-base: 0.875rem;
--text-lg: 1rem;
--text-xl: 1.25rem;
--border-subtle: rgba(255, 255, 255, 0.08);
--border-default: rgba(255, 255, 255, 0.12);
--border-strong: rgba(255, 255, 255, 0.2);
--radius-sm: 4px;
--radius-md: 6px;
--radius-lg: 8px;
--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
--duration-fast: 150ms;
--duration-normal: 250ms;
}