layout-dashboard Internal Tools Design
Use when building or modifying admin dashboards, internal tools, ops panels, or back-office UIs. Enforces scannability, data density, tactile feedback, and dark-mode-safe patterns calibrated for daily-use operational tools.
Install this skill
git submodule add https://github.com/modh-labs/playbook.git .playbookOne submodule installs all 17 skills. Reference .playbook/skills/internal-tools-design/SKILL.md from your AGENTS.md.
Internal Tools Design
Calibrated for ops dashboards, admin panels, and back-office tools where the user checks this page 20 times a day. Every decision optimizes for scannability, speed, and reliability — not visual impact.
Rule count: 12. Rules 11 (Flatten Before Expand) and 12 (Tri-State Picker) are newer and frequently violated — review them first on any admin UI.
Baseline Configuration
DESIGN_VARIANCE: 3 (predictable, symmetrical grids)
MOTION_INTENSITY: 2 (CSS transitions only, no libraries)
VISUAL_DENSITY: 6 (dense but readable, monospace numbers)
These values are non-negotiable for internal tools. If the user asks for a marketing page or public-facing UI, use a different design skill instead.
The 12 Rules
1. Data Hierarchy Over Decoration
Every pixel serves the question: "Can the ops person find what they need in <2 seconds?"
- Numbers are king. Large, bold,
font-mono. The first thing the eye hits. - Labels are secondary.
text-sm font-medium text-muted-foregroundabove the number. - Trends are tertiary. Small percentage or sparkline beside the number, not a separate chart.
- No decorative elements. No gradients, no illustrations, no hero sections.
Good: 250 users (large mono) -> 35% coverage (small muted below)
Bad: Users card with gradient background and decorative user icon taking 40% of the card
2. Consistent Page Anatomy
Every internal tool page follows this exact structure:
<div className="space-y-8 p-6">
{/* 1. Header: title + description + action buttons */}
<div className="flex items-center justify-between">
<div className="space-y-1">
<h1 className="text-3xl font-bold tracking-tight">Page Title</h1>
<p className="text-muted-foreground">One-line description</p>
</div>
<div className="flex items-center gap-2">
{/* Export, Refresh, Create buttons */}
</div>
</div>
{/* 2. Stats cards (if applicable) */}
<div className="grid gap-4 sm:grid-cols-3">
{/* MetricCard components */}
</div>
{/* 3. Main content (table, list, or detail view) */}
<Card className="border-border/50 shadow-sm">
{/* Search/filter bar -> data table */}
</Card>
</div>
Deviate from this structure only with explicit justification.
3. Typography Rules
| Element | Classes | Never |
|---|---|---|
| Page title | text-3xl font-bold tracking-tight | font-semibold, text-2xl |
| Card title | text-lg font-semibold | text-xl, font-bold |
| Body text | text-sm | text-base in dense tables |
| Muted/secondary | text-muted-foreground | Hardcoded grays |
| Numbers, IDs, dates, emails, phones | font-mono | Sans-serif for data |
| Table headers | text-sm font-medium | font-bold on headers |
4. Color System (Dark Mode First)
Never use hardcoded colors. Always pair light + dark:
| Use Case | Pattern |
|---|---|
| Success/healthy | text-emerald-600 dark:text-emerald-400 + bg-emerald-50 dark:bg-emerald-950/20 |
| Error/critical | text-red-600 dark:text-red-400 + bg-red-50 dark:bg-red-950/20 |
| Warning | text-amber-600 dark:text-amber-400 + bg-amber-50 dark:bg-amber-950/20 |
| Info | text-blue-600 dark:text-blue-400 + bg-blue-50 dark:bg-blue-950/20 |
| Neutral surface | bg-muted |
| Neutral text | text-muted-foreground |
| Borders | border-border or border-border/50 |
Banned: bg-gray-*, bg-slate-*, text-gray-* without dark variant. Use semantic tokens.
5. Card & Surface Rules
- Cards use
border-border/50 shadow-sm— never bareborderor heavyshadow-md - Stats cards: icon in
h-12 w-12 rounded-full bg-{color}-100 dark:bg-{color}-950/30circle - Table wrappers:
rounded-lg border border-border/50 overflow-hidden - Table headers:
bg-muted/50 hover:bg-muted/50 - Table rows:
py-3cell padding,hover:bg-muted/30 transition-colors
6. Interactive Feedback (CSS Only)
No Framer Motion. No animation libraries. Pure CSS:
/* Buttons: tactile press */
.button { @apply active:scale-[0.98] transition-all; }
/* Table rows: scannable highlight */
.row { @apply hover:bg-muted/30 transition-colors; }
/* Sortable headers: cursor + hover */
.sort-header { @apply cursor-pointer hover:bg-muted/80 transition-colors; }
/* Refresh spinner: only during loading */
.refresh-icon { @apply animate-spin; } /* only when isLoading */
/* Copy button: appear on row hover */
.copy { @apply opacity-0 group-hover/row:opacity-100 transition-opacity; }
Banned for internal tools: Framer Motion, GSAP, staggered reveals, magnetic buttons, parallax, spring physics. These slow down daily-use interfaces.
7. Empty, Loading, Error States
Every page needs all three. No exceptions.
Loading: Skeleton matching the exact page layout. Never a centered spinner.
// Skeleton rows should match table column widths
<Skeleton className="h-4 w-28" /> // name column
<Skeleton className="h-4 w-40" /> // email column (wider)
<Skeleton className="h-5 w-16 rounded-full" /> // badge column
Empty: Centered icon + title + subtitle. Never just text.
<div className="flex flex-col items-center justify-center py-16">
<div className="flex h-16 w-16 items-center justify-center rounded-full bg-muted mb-4">
<Icon className="h-8 w-8 text-muted-foreground" />
</div>
<p className="text-muted-foreground font-medium">No items found</p>
<p className="text-sm text-muted-foreground mt-1">Helpful next step</p>
</div>
Error: Capture to error tracking + user-friendly boundary with retry button.
<div className="mx-auto h-12 w-12 rounded-full bg-destructive/10 flex items-center justify-center mb-4">
<AlertCircle className="h-6 w-6 text-destructive" />
</div>
8. Table Design
Tables are the primary data display for internal tools.
- Sortable columns: Click header to sort. Show
ArrowUpDown(inactive) orArrowUp/ArrowDown(active). - Search bar: Always
pl-10withSearchicon positioned absolutely atleft-3 top-1/2 -translate-y-1/2 - Filter dropdowns:
w-[170px]Select beside search bar - Row actions: Appear on hover or in a
DropdownMenuon the last column - Grouped cells: Avatar + name + email in one cell for identity data (not 3 separate columns)
- Relative timestamps:
font-mono text-muted-foregroundwith absolute time in aTooltip - Status badges: Use Badge component with semantic colors from Rule 4
- Pagination info: Show "X of Y items" in CardDescription
9. Component Rules
- Always use your project's component library (e.g., shadcn/ui). Never raw HTML.
- Always
cursor-pointeron clickable elements (buttons, links, sortable headers, selects) - Always use a utility for conditional classes (e.g.,
cn(),clsx()) - Never barrel imports
- Never inline
style={}— utility classes only - Icons: Use a consistent icon library at default size.
h-4 w-4in buttons/badges,h-6 w-6in stat card circles
10. Responsive Behavior
Internal tools are primarily desktop. But don't break on tablet.
- Stats grids:
grid gap-4 sm:grid-cols-2 lg:grid-cols-3(orlg:grid-cols-4) - Search bars:
min-w-[280px]withflex-1 - Tables: horizontal scroll on mobile (wrap in
overflow-x-auto) - Sidebar: collapsible with persisted state in localStorage
11. Flatten Before You Expand
Progressive disclosure — hiding secondary controls behind an expand/ collapse chevron — is a consumer-UI reflex that usually makes internal tools worse. Ops users scan rows, not chevrons. Every expand state is a click-to-discover that the next ops user doesn't know about.
Default rule: if every row in a list has a body, don't collapse it. Render every control visible inline. Density is the feature.
Only use expand/collapse when:
- The body is genuinely optional (not every row has one)
- The body is heavy enough to hurt scroll performance if always mounted
- The ops task primarily involves scanning headers, and bodies are the exception
If you've hit two of those three, use <details> or shadcn
Collapsible with data-state exposed so tests can target it. If you
haven't hit two of three, flatten. Dense beats clever.
Signal to watch for in review: a form or editor with useState
tracking isExpanded per row is usually a flatten candidate. The
state is serving complexity, not the user.
12. Tri-State Picker over Dual-Toggle
Any time a field has two booleans that together express one concept —
{ enabled, required }, { visible, active }, { included, locked }
— collapse them into a single segmented picker with one segment per
real state. Three switches on one row is worse than one three-segment
control.
The classic trap: you start with a Required? switch. Then someone
asks for an Include in form? toggle. You add it as a second switch.
Now the row has two switches with subtle interactions ("if not
included, required doesn't matter"). Ops users have to learn a
mini-rulebook. Disable logic for the dependent toggle is implicit.
Fix: map the real states onto one picker.
Instead of: [ Include ✓ ] [ Required ✓ ]
Use: [ Required | Optional | Disabled ]
All three states are always reachable. The dependent-toggle UX trap
disappears. Screen readers announce a radiogroup with a clear
accessible name. Tests target by getByRole("radio", { name: /required/i }).
Supports a mode prop (tri | binary | locked) so different row
types can show different subsets without forking the component:
tri— all three segments (the full picker)binary— just Required + Optional (custom fields that can't be disabled at this level)locked— single pinned segment, read-only (structurally required fields that can't be toggled at all)
Reference: a tri-state FieldRequirementPicker shipped in Aura's
scheduler booking-form builder, replacing two switches plus a hidden
dependency between them.
Anti-Patterns (Forbidden in Internal Tools)
| Pattern | Why It's Banned | Use Instead |
|---|---|---|
| Framer Motion | Adds bundle size + complexity for zero ops value | CSS transition-colors |
| Staggered reveals | Slows down scanning — ops needs instant render | Immediate mount |
| Glassmorphism/blur | Reduces readability, expensive GPU paint | Solid bg-card surfaces |
| Gradient text | Illegible on data-dense pages | Solid text-foreground |
| Hero sections | No one browses an admin dashboard | Jump straight to data |
| Magnetic buttons | Ops clicks 100 buttons/day, magnets slow them down | Standard click targets |
| Oversized H1 | Admin pages aren't landing pages | text-3xl max |
| Decorative illustrations | Wastes space where data should be | Use the space for metrics |
| Skeleton shimmer animation | Distracting on 20+ skeleton elements | Static bg-muted skeleton |
| Center-aligned layouts | Wastes horizontal space on wide monitors | Left-aligned content |
Quick Audit Checklist
Run through this for every internal tool page before shipping:
- Page follows the 4-section anatomy (header -> stats -> filters -> data)
- All numbers use
font-mono - All colors have
dark:variants - All buttons have
cursor-pointer - Loading skeleton matches page layout
- Empty state has icon + title + subtitle
- Error boundary captures to error tracking service
- Tables have sortable headers with sort icons
- Search input has
pl-10with Search icon - Interactive elements have
transition-colorsortransition-all - No hardcoded gray/slate/green/red without dark variant
- No Framer Motion or animation libraries imported
- No raw HTML elements (
<button>,<input>,<select>) - Timestamps show relative with absolute in tooltip
Related Skills
Code Review
Run educational code reviews against your project's quality standards. Use when reviewing PRs, checking current branch before pushing, or doing batch quality sweeps across all open PRs. Applies observability, testing, SOLID, type safety, security, business logic, and clean code checks with pass/fail verdicts. Produces actionable findings with real-world impact explanations, not just "best practice" citations.
Universalsettings-2Cross-Editor AI Setup
Ensure AI configuration works across Claude Code, Cursor, Copilot, Windsurf, and Codex. Use when creating AGENTS.md files, adding skills, setting up project rules, or discussing cross-editor compatibility. Enforces the @import convention and single-source-of-truth strategy.
UniversalpalettePremium UI Design Engineering
Premium UI/UX design engineering skill. Overrides default LLM design biases with tunable dials for variance, motion, and density. Enforces anti-AI-tell patterns, strict typography rules, color calibration, layout diversification, performance guardrails, and comprehensive design audit checklists. Framework-agnostic.
Universal