Modh
AboutServicesWorkPlaybookResourcesBook a call
Book a call
Agent Skills/Universal/Internal Tools Design
Universalintermediate9 min

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 .playbook

One 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-foreground above 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

ElementClassesNever
Page titletext-3xl font-bold tracking-tightfont-semibold, text-2xl
Card titletext-lg font-semiboldtext-xl, font-bold
Body texttext-smtext-base in dense tables
Muted/secondarytext-muted-foregroundHardcoded grays
Numbers, IDs, dates, emails, phonesfont-monoSans-serif for data
Table headerstext-sm font-mediumfont-bold on headers

4. Color System (Dark Mode First)

Never use hardcoded colors. Always pair light + dark:

Use CasePattern
Success/healthytext-emerald-600 dark:text-emerald-400 + bg-emerald-50 dark:bg-emerald-950/20
Error/criticaltext-red-600 dark:text-red-400 + bg-red-50 dark:bg-red-950/20
Warningtext-amber-600 dark:text-amber-400 + bg-amber-50 dark:bg-amber-950/20
Infotext-blue-600 dark:text-blue-400 + bg-blue-50 dark:bg-blue-950/20
Neutral surfacebg-muted
Neutral texttext-muted-foreground
Bordersborder-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 bare border or heavy shadow-md
  • Stats cards: icon in h-12 w-12 rounded-full bg-{color}-100 dark:bg-{color}-950/30 circle
  • Table wrappers: rounded-lg border border-border/50 overflow-hidden
  • Table headers: bg-muted/50 hover:bg-muted/50
  • Table rows: py-3 cell 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) or ArrowUp/ArrowDown (active).
  • Search bar: Always pl-10 with Search icon positioned absolutely at left-3 top-1/2 -translate-y-1/2
  • Filter dropdowns: w-[170px] Select beside search bar
  • Row actions: Appear on hover or in a DropdownMenu on the last column
  • Grouped cells: Avatar + name + email in one cell for identity data (not 3 separate columns)
  • Relative timestamps: font-mono text-muted-foreground with absolute time in a Tooltip
  • 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-pointer on 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-4 in buttons/badges, h-6 w-6 in 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 (or lg:grid-cols-4)
  • Search bars: min-w-[280px] with flex-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)

PatternWhy It's BannedUse Instead
Framer MotionAdds bundle size + complexity for zero ops valueCSS transition-colors
Staggered revealsSlows down scanning — ops needs instant renderImmediate mount
Glassmorphism/blurReduces readability, expensive GPU paintSolid bg-card surfaces
Gradient textIllegible on data-dense pagesSolid text-foreground
Hero sectionsNo one browses an admin dashboardJump straight to data
Magnetic buttonsOps clicks 100 buttons/day, magnets slow them downStandard click targets
Oversized H1Admin pages aren't landing pagestext-3xl max
Decorative illustrationsWastes space where data should beUse the space for metrics
Skeleton shimmer animationDistracting on 20+ skeleton elementsStatic bg-muted skeleton
Center-aligned layoutsWastes horizontal space on wide monitorsLeft-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-10 with Search icon
  • Interactive elements have transition-colors or transition-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

search-check

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.

Universal
settings-2

Cross-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.

Universal
palette

Premium 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
←Back to Agent Skills