dashboard-design
About
This skill provides dashboard design patterns and implementation guidance for creating analytics interfaces and admin panels. It covers layout strategies, KPI displays, responsive grids, and real-time data visualization. Developers should use it when building monitoring dashboards, analytics interfaces, or admin panels that require clear data presentation.
Quick Install
Claude Code
Recommended/plugin add https://github.com/majiayu000/claude-skill-registrygit clone https://github.com/majiayu000/claude-skill-registry.git ~/.claude/skills/dashboard-designCopy and paste this command in Claude Code to install this skill
Documentation
Dashboard Design
Create effective, information-rich dashboards that surface key data clearly.
Instructions
- Prioritize information - Most important metrics at top-left
- Use consistent card layouts - Same styling for similar data types
- Design for scanning - Users glance, not read; make data obvious
- Show context - Compare to previous periods, show trends
- Enable action - Dashboards should lead to decisions
Dashboard Layout Patterns
Standard Admin Dashboard
function AdminDashboard() {
return (
<div className="min-h-screen bg-gray-100 dark:bg-gray-900">
{/* Top Navigation */}
<header className="sticky top-0 z-50 bg-white dark:bg-gray-800 border-b shadow-sm">
<div className="flex items-center justify-between h-16 px-6">
<Logo />
<div className="flex items-center gap-4">
<SearchInput />
<NotificationBell />
<UserMenu />
</div>
</div>
</header>
<div className="flex">
{/* Sidebar Navigation */}
<aside className="hidden lg:block w-64 bg-white dark:bg-gray-800 border-r min-h-[calc(100vh-4rem)] sticky top-16">
<nav className="p-4 space-y-2">
<SidebarLink icon={HomeIcon} label="Overview" active />
<SidebarLink icon={ChartIcon} label="Analytics" />
<SidebarLink icon={UsersIcon} label="Customers" />
<SidebarLink icon={SettingsIcon} label="Settings" />
</nav>
</aside>
{/* Main Content */}
<main className="flex-1 p-6">
{/* Page Header */}
<div className="mb-6">
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">
Dashboard Overview
</h1>
<p className="text-gray-500">Welcome back, here's what's happening</p>
</div>
{/* KPI Cards Row */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 mb-6">
<KPICard
title="Total Revenue"
value="$45,231"
change="+12.5%"
trend="up"
/>
<KPICard
title="Active Users"
value="2,345"
change="+5.2%"
trend="up"
/>
<KPICard
title="Conversion Rate"
value="3.2%"
change="-0.4%"
trend="down"
/>
<KPICard
title="Avg. Order Value"
value="$127"
change="+8.1%"
trend="up"
/>
</div>
{/* Charts Row */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
<ChartCard title="Revenue Over Time">
<LineChart data={revenueData} />
</ChartCard>
<ChartCard title="Sales by Category">
<BarChart data={categoryData} />
</ChartCard>
</div>
{/* Data Table */}
<Card>
<CardHeader>
<CardTitle>Recent Orders</CardTitle>
<Button variant="outline" size="sm">View All</Button>
</CardHeader>
<DataTable
columns={orderColumns}
data={recentOrders}
pagination
/>
</Card>
</main>
</div>
</div>
);
}
KPI Card Component
interface KPICardProps {
title: string;
value: string | number;
change?: string;
trend?: 'up' | 'down' | 'neutral';
icon?: React.ComponentType;
subtitle?: string;
}
function KPICard({ title, value, change, trend, icon: Icon, subtitle }: KPICardProps) {
return (
<div className="bg-white dark:bg-gray-800 rounded-xl p-6 shadow-sm border border-gray-200 dark:border-gray-700">
<div className="flex items-start justify-between">
<div>
<p className="text-sm font-medium text-gray-500 dark:text-gray-400">
{title}
</p>
<p className="mt-2 text-3xl font-bold text-gray-900 dark:text-white">
{value}
</p>
{change && (
<div className="mt-2 flex items-center gap-1">
{trend === 'up' && (
<ArrowUpIcon className="w-4 h-4 text-green-500" />
)}
{trend === 'down' && (
<ArrowDownIcon className="w-4 h-4 text-red-500" />
)}
<span className={`text-sm font-medium ${
trend === 'up' ? 'text-green-600' :
trend === 'down' ? 'text-red-600' :
'text-gray-500'
}`}>
{change}
</span>
<span className="text-sm text-gray-400">vs last month</span>
</div>
)}
</div>
{Icon && (
<div className="p-3 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
<Icon className="w-6 h-6 text-blue-600 dark:text-blue-400" />
</div>
)}
</div>
</div>
);
}
Chart Card Component
interface ChartCardProps {
title: string;
subtitle?: string;
action?: React.ReactNode;
children: React.ReactNode;
}
function ChartCard({ title, subtitle, action, children }: ChartCardProps) {
return (
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden">
<div className="flex items-center justify-between px-6 py-4 border-b border-gray-200 dark:border-gray-700">
<div>
<h3 className="font-semibold text-gray-900 dark:text-white">{title}</h3>
{subtitle && (
<p className="text-sm text-gray-500">{subtitle}</p>
)}
</div>
{action}
</div>
<div className="p-6">
{children}
</div>
</div>
);
}
Dashboard Grid Patterns
Responsive Dashboard Grid
// 12-column grid system
<div className="grid grid-cols-12 gap-6">
{/* Full width */}
<div className="col-span-12">
<PageHeader />
</div>
{/* 4 equal KPI cards */}
<div className="col-span-12 sm:col-span-6 lg:col-span-3">
<KPICard />
</div>
<div className="col-span-12 sm:col-span-6 lg:col-span-3">
<KPICard />
</div>
<div className="col-span-12 sm:col-span-6 lg:col-span-3">
<KPICard />
</div>
<div className="col-span-12 sm:col-span-6 lg:col-span-3">
<KPICard />
</div>
{/* 2/3 + 1/3 layout */}
<div className="col-span-12 lg:col-span-8">
<MainChart />
</div>
<div className="col-span-12 lg:col-span-4">
<SidePanel />
</div>
{/* 50/50 split */}
<div className="col-span-12 md:col-span-6">
<ChartA />
</div>
<div className="col-span-12 md:col-span-6">
<ChartB />
</div>
</div>
Real-Time Dashboard
function RealTimeDashboard() {
const [metrics, setMetrics] = useState<Metrics | null>(null);
useEffect(() => {
// WebSocket for real-time updates
const ws = new WebSocket('wss://api.example.com/metrics');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
setMetrics(data);
};
return () => ws.close();
}, []);
return (
<div className="space-y-6">
{/* Live indicator */}
<div className="flex items-center gap-2">
<span className="relative flex h-3 w-3">
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75" />
<span className="relative inline-flex rounded-full h-3 w-3 bg-green-500" />
</span>
<span className="text-sm text-gray-500">Live</span>
<span className="text-sm text-gray-400">
Updated {formatRelativeTime(metrics?.timestamp)}
</span>
</div>
{/* Real-time metrics */}
<div className="grid grid-cols-4 gap-4">
<LiveMetric
label="Active Users"
value={metrics?.activeUsers}
sparkline={metrics?.userHistory}
/>
<LiveMetric
label="Requests/sec"
value={metrics?.requestsPerSecond}
unit="req/s"
/>
<LiveMetric
label="Avg Response"
value={metrics?.avgResponseTime}
unit="ms"
/>
<LiveMetric
label="Error Rate"
value={metrics?.errorRate}
unit="%"
alert={metrics?.errorRate > 1}
/>
</div>
</div>
);
}
Data Table for Dashboards
interface DataTableProps<T> {
columns: ColumnDef<T>[];
data: T[];
pagination?: boolean;
searchable?: boolean;
actions?: (row: T) => React.ReactNode;
}
function DataTable<T>({ columns, data, pagination, searchable }: DataTableProps<T>) {
const [search, setSearch] = useState('');
const [page, setPage] = useState(1);
const pageSize = 10;
const filteredData = useMemo(() => {
if (!search) return data;
return data.filter(row =>
Object.values(row).some(val =>
String(val).toLowerCase().includes(search.toLowerCase())
)
);
}, [data, search]);
const paginatedData = useMemo(() => {
if (!pagination) return filteredData;
const start = (page - 1) * pageSize;
return filteredData.slice(start, start + pageSize);
}, [filteredData, page, pagination]);
return (
<div>
{searchable && (
<div className="mb-4">
<input
type="search"
placeholder="Search..."
value={search}
onChange={(e) => setSearch(e.target.value)}
className="px-4 py-2 border rounded-lg w-full max-w-sm"
/>
</div>
)}
<div className="overflow-x-auto">
<table className="w-full">
<thead className="bg-gray-50 dark:bg-gray-800">
<tr>
{columns.map(col => (
<th key={col.key} className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
{col.header}
</th>
))}
</tr>
</thead>
<tbody className="divide-y divide-gray-200 dark:divide-gray-700">
{paginatedData.map((row, i) => (
<tr key={i} className="hover:bg-gray-50 dark:hover:bg-gray-800">
{columns.map(col => (
<td key={col.key} className="px-6 py-4 whitespace-nowrap text-sm">
{col.render ? col.render(row) : row[col.key]}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
{pagination && (
<Pagination
page={page}
totalPages={Math.ceil(filteredData.length / pageSize)}
onPageChange={setPage}
/>
)}
</div>
);
}
Best Practices
- 5-second rule - Key metrics should be understood in 5 seconds
- Above the fold - Critical data visible without scrolling
- Consistent time ranges - All charts use same time period
- Progressive disclosure - Summary → details on demand
- Empty states - Show meaningful content when no data
- Loading states - Skeleton screens while loading
When to Use
- Building admin panels and back-office tools
- Creating analytics dashboards
- Monitoring systems and real-time displays
- Data-heavy business applications
- Internal tools and management interfaces
Notes
- Consider user role - executives vs analysts have different needs
- Mobile dashboards need different layouts, not just responsive
- Performance matters - virtualize long lists, lazy load charts
- Allow customization - users can arrange their own dashboards
GitHub Repository
Related Skills
content-collections
MetaThis skill provides a production-tested setup for Content Collections, a TypeScript-first tool that transforms Markdown/MDX files into type-safe data collections with Zod validation. Use it when building blogs, documentation sites, or content-heavy Vite + React applications to ensure type safety and automatic content validation. It covers everything from Vite plugin configuration and MDX compilation to deployment optimization and schema validation.
creating-opencode-plugins
MetaThis skill provides the structure and API specifications for creating OpenCode plugins that hook into 25+ event types like commands, files, and LSP operations. It offers implementation patterns for JavaScript/TypeScript modules that intercept and extend the AI assistant's lifecycle. Use it when you need to build event-driven plugins for monitoring, custom handling, or extending OpenCode's capabilities.
langchain
MetaLangChain is a framework for building LLM applications using agents, chains, and RAG pipelines. It supports multiple LLM providers, offers 500+ integrations, and includes features like tool calling and memory management. Use it for rapid prototyping and deploying production systems like chatbots, autonomous agents, and question-answering services.
cloudflare-turnstile
MetaThis skill provides comprehensive guidance for implementing Cloudflare Turnstile as a CAPTCHA-alternative bot protection system. It covers integration for forms, login pages, API endpoints, and frameworks like React/Next.js/Hono, while handling invisible challenges that maintain user experience. Use it when migrating from reCAPTCHA, debugging error codes, or implementing token validation and E2E tests.
