Theme
← Examples
Product Dashboard
A dense workspace surface with KPI cards, tabs, team context, tables, and quick actions.
apps/docs/src/lib/demos/DashboardExampleDemo.svelte
Preview
Open in new tabAnalytics Dashboard
March 2026 — live dataNew Report
Schedule Export
Settings
Revenue $84,320
+12% vs last month
Active Users 24,891
+8% vs last month
New Orders 1,482
-3% vs last month
Conversion Rate 3.64%
+0.4% vs last month
Recent Orders
| Order | Customer | Product | Amount | Status |
|---|---|---|---|---|
| #ORD-1042 | Alice Johnson [email protected] | Pro Plan | $299.00 | Completed |
| #ORD-1041 | Marcus Lee [email protected] | Starter Plan | $49.00 | Processing |
| #ORD-1040 | Priya Patel [email protected] | Enterprise | $1,200.00 | Completed |
| #ORD-1039 | James Okafor [email protected] | Pro Plan | $299.00 | Pending |
| #ORD-1038 | Sofia Andersen [email protected] | Starter Plan | $49.00 | Cancelled |
Source
This page is rendered from the real demo file shown below.
svelte
<script lang="ts">
import {
Badge,
Button,
Card,
Flex,
Grid,
Progress,
Separator,
Stack,
Table,
Tabs,
Tooltip,
DropdownMenu,
User,
} from '@dryui/ui';
type OrderStatus = 'Completed' | 'Processing' | 'Pending' | 'Cancelled';
type MemberStatus = 'Online' | 'Away' | 'Offline';
let activeTab = $state('overview');
const stats = [
{
label: 'Revenue',
value: '$84,320',
change: '+12%',
changeColor: 'green' as const,
description: 'vs last month',
},
{
label: 'Active Users',
value: '24,891',
change: '+8%',
changeColor: 'green' as const,
description: 'vs last month',
},
{
label: 'New Orders',
value: '1,482',
change: '-3%',
changeColor: 'red' as const,
description: 'vs last month',
},
{
label: 'Conversion Rate',
value: '3.64%',
change: '+0.4%',
changeColor: 'green' as const,
description: 'vs last month',
},
];
const recentOrders: Array<{
id: string;
customer: string;
email: string;
product: string;
amount: string;
status: OrderStatus;
}> = [
{
id: '#ORD-1042',
customer: 'Alice Johnson',
email: '[email protected]',
product: 'Pro Plan',
amount: '$299.00',
status: 'Completed',
},
{
id: '#ORD-1041',
customer: 'Marcus Lee',
email: '[email protected]',
product: 'Starter Plan',
amount: '$49.00',
status: 'Processing',
},
{
id: '#ORD-1040',
customer: 'Priya Patel',
email: '[email protected]',
product: 'Enterprise',
amount: '$1,200.00',
status: 'Completed',
},
{
id: '#ORD-1039',
customer: 'James Okafor',
email: '[email protected]',
product: 'Pro Plan',
amount: '$299.00',
status: 'Pending',
},
{
id: '#ORD-1038',
customer: 'Sofia Andersen',
email: '[email protected]',
product: 'Starter Plan',
amount: '$49.00',
status: 'Cancelled',
},
];
const statusColor: Record<OrderStatus, 'green' | 'blue' | 'yellow' | 'red'> = {
Completed: 'green',
Processing: 'blue',
Pending: 'yellow',
Cancelled: 'red',
};
const categories = [
{ name: 'SaaS Subscriptions', value: 68, color: 'blue' as const },
{ name: 'One-time Licenses', value: 45, color: 'red' as const },
{ name: 'Support Contracts', value: 82, color: 'green' as const },
{ name: 'Professional Services', value: 31, color: 'yellow' as const },
{ name: 'Add-ons & Extras', value: 57, color: 'yellow' as const },
];
const teamMembers: Array<{
name: string;
role: string;
status: MemberStatus;
avatar?: string;
}> = [
{ name: 'Elena Rossi', role: 'Engineering Lead', status: 'Online' },
{ name: 'David Kim', role: 'Product Designer', status: 'Online' },
{ name: 'Yuki Tanaka', role: 'Backend Engineer', status: 'Away' },
{ name: 'Amara Nwosu', role: 'Data Analyst', status: 'Online' },
{ name: 'Tom Bergmann', role: 'DevOps Engineer', status: 'Offline' },
{ name: 'Chloe Martin', role: 'QA Engineer', status: 'Away' },
];
const memberStatusColor: Record<MemberStatus, 'green' | 'yellow' | 'gray'> = {
Online: 'green',
Away: 'yellow',
Offline: 'gray',
};
</script>
<Stack gap="lg">
<Flex justify="between" align="center">
<Stack gap="sm">
<h2 class="dashboard-title">Analytics Dashboard</h2>
<span class="dashboard-subtitle">March 2026 — live data</span>
</Stack>
<Flex gap="sm" align="center">
<Button variant="outline" size="sm">Export CSV</Button>
<DropdownMenu.Root>
<DropdownMenu.Trigger>
<Button variant="solid" size="sm">Actions</Button>
</DropdownMenu.Trigger>
<DropdownMenu.Content offset={8}>
<DropdownMenu.Item>New Report</DropdownMenu.Item>
<DropdownMenu.Item>Schedule Export</DropdownMenu.Item>
<DropdownMenu.Separator />
<DropdownMenu.Item>Settings</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>
</Flex>
</Flex>
<!-- Stats row -->
<Grid columns={4} gap="md">
{#each stats as stat (stat.label)}
<Card.Root>
<Card.Content>
<Stack gap="sm">
<span class="stat-label">{stat.label}</span>
<span class="stat-value">{stat.value}</span>
<Flex gap="sm" align="center">
<Badge color={stat.changeColor} variant="soft" size="sm">
{stat.change}
</Badge>
<span class="stat-desc">{stat.description}</span>
</Flex>
</Stack>
</Card.Content>
</Card.Root>
{/each}
</Grid>
<!-- Main content + Sidebar -->
<Flex gap="lg" align="start">
<!-- Main content: Tabs -->
<div class="main-content">
<Tabs.Root bind:value={activeTab}>
<Tabs.List>
<Tabs.Trigger value="overview">Overview</Tabs.Trigger>
<Tabs.Trigger value="analytics">Analytics</Tabs.Trigger>
</Tabs.List>
<!-- Overview tab: recent orders table -->
<Tabs.Content value="overview">
<Card.Root>
<Card.Header>
<Flex justify="between" align="center">
<h3 class="card-title">Recent Orders</h3>
<Button variant="ghost" size="sm">View all</Button>
</Flex>
</Card.Header>
<Card.Content>
<Table.Root>
<Table.Header>
<Table.Row>
<Table.Head scope="col">Order</Table.Head>
<Table.Head scope="col">Customer</Table.Head>
<Table.Head scope="col">Product</Table.Head>
<Table.Head scope="col">Amount</Table.Head>
<Table.Head scope="col">Status</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
{#each recentOrders as order (order.id)}
<Table.Row>
<Table.Cell>
<span class="order-id">{order.id}</span>
</Table.Cell>
<Table.Cell>
<User name={order.customer} description={order.email} avatar={{ fallback: order.customer.charAt(0) }} size="sm" />
</Table.Cell>
<Table.Cell>{order.product}</Table.Cell>
<Table.Cell>
<span class="order-amount">{order.amount}</span>
</Table.Cell>
<Table.Cell>
<Badge
color={statusColor[order.status]}
variant="soft"
size="sm"
>
{order.status}
</Badge>
</Table.Cell>
</Table.Row>
{/each}
</Table.Body>
</Table.Root>
</Card.Content>
</Card.Root>
</Tabs.Content>
<!-- Analytics tab: category progress bars -->
<Tabs.Content value="analytics">
<Card.Root>
<Card.Header>
<h3 class="card-title">Revenue by Category</h3>
</Card.Header>
<Card.Content>
<Stack gap="md">
{#each categories as cat (cat.name)}
<Stack gap="sm">
<Flex justify="between" align="center">
<span class="cat-name">{cat.name}</span>
<span class="cat-pct">{cat.value}%</span>
</Flex>
<Progress value={cat.value} color={cat.color} size="sm" />
</Stack>
{/each}
</Stack>
</Card.Content>
</Card.Root>
</Tabs.Content>
</Tabs.Root>
</div>
<!-- Sidebar: team members -->
<div class="sidebar">
<Card.Root>
<Card.Header>
<Flex justify="between" align="center">
<h3 class="card-title">Team</h3>
<Badge color="blue" variant="soft" size="sm">
{teamMembers.filter((m) => m.status === 'Online').length} online
</Badge>
</Flex>
</Card.Header>
<Card.Content>
<Stack gap="md">
{#each teamMembers as member, i (member.name)}
{#if i > 0}
<Separator />
{/if}
<User
name={member.name}
description={member.role}
avatar={{ fallback: member.name.charAt(0) }}
chip={{ color: memberStatusColor[member.status], label: member.status }}
size="sm"
/>
{/each}
</Stack>
</Card.Content>
<Card.Footer>
<Button variant="outline" size="sm">Manage team</Button>
</Card.Footer>
</Card.Root>
</div>
</Flex>
</Stack>
<style>
.dashboard-title {
margin: 0;
font-size: var(--dry-text-2xl-size);
font-weight: 700;
}
.dashboard-subtitle {
font-size: var(--dry-text-sm-size);
color: var(--dry-color-text-secondary);
}
.stat-label {
font-size: var(--dry-text-sm-size);
color: var(--dry-color-text-secondary);
}
.stat-value {
font-size: var(--dry-text-2xl-size);
font-weight: 700;
line-height: 1.2;
}
.stat-desc {
font-size: var(--dry-text-xs-size);
color: var(--dry-color-text-secondary);
}
.card-title {
margin: 0;
font-size: var(--dry-text-base-size);
font-weight: 600;
}
.order-id {
font-size: var(--dry-text-sm-size);
color: var(--dry-color-text-secondary);
font-family: var(--dry-font-mono, monospace);
}
.order-amount {
font-weight: 500;
}
.cat-name {
font-size: var(--dry-text-sm-size);
font-weight: 500;
}
.cat-pct {
font-size: var(--dry-text-sm-size);
color: var(--dry-color-text-secondary);
min-width: 2.5rem;
text-align: right;
}
.main-content {
flex: 1;
min-width: 0;
}
.sidebar {
width: 280px;
flex-shrink: 0;
}
</style>