Lab

Dashboard Lab

Seven admin UI patterns — stats overviews, kanban boards, inboxes, calendars, user tables, and settings panels.

1. Stats Overview — sidebar + top bar + stat cards (classic admin layout)

Dashboard

AL
Users
8,247
↑ 12.4% this month
Revenue
$42.8k
↑ 8.1%
Orders
1,128
↓ 3.2%
Conversion
4.72%
↑ 21%

Welcome back, Ada 👋

You have 3 new messages and 12 pending tasks.

<div class="dash-layout">
  <aside class="side">
    <div class="brand">moonbase</div>
    <a class="active">Dashboard</a>
    <a>Team</a>
    <a>Projects</a>
    <a>Settings</a>
  </aside>

  <div>
    <header class="topbar">
      <h2>Dashboard</h2>
      <div class="right"><div class="avatar">AL</div></div>
    </header>
    <div class="main">
      <div class="stats">
        <div class="stat"><div class="label">Users</div><div class="value">8,247</div><div class="delta up">↑ 12.4% this month</div></div>
        <div class="stat"><div class="label">Revenue</div><div class="value">$42.8k</div><div class="delta up">↑ 8.1%</div></div>
        <div class="stat"><div class="label">Orders</div><div class="value">1,128</div><div class="delta down">↓ 3.2%</div></div>
        <div class="stat"><div class="label">Conversion</div><div class="value">4.72%</div><div class="delta up">↑ 21%</div></div>
      </div>
      <div class="welcome">
        <h3>Welcome back, Ada 👋</h3>
        <p>You have 3 new messages and 12 pending tasks.</p>
      </div>
    </div>
  </div>
</div>
.dash-layout {
  display: grid;
  grid-template-columns: 220px 1fr;
  min-height: 100vh;
}

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 14px;
}
// Count-up animation: tween each stat value from 0 to its final number.
function countUp(el, target, duration = 1200) {
  const start = performance.now();
  const isCurrency = el.textContent.includes('$');
  const isPercent  = el.textContent.includes('%');

  function tick(now) {
    const progress = Math.min((now - start) / duration, 1);
    const eased = 1 - Math.pow(1 - progress, 3); // ease-out cubic
    const current = target * eased;

    let text;
    if (isCurrency) text = '$' + current.toFixed(1) + 'k';
    else if (isPercent) text = current.toFixed(2) + '%';
    else text = Math.floor(current).toLocaleString();

    el.textContent = text;
    if (progress < 1) requestAnimationFrame(tick);
  }
  requestAnimationFrame(tick);
}

// Read the final number from each .stat .value and animate to it.
document.querySelectorAll('.stat .value').forEach(el => {
  const raw = el.textContent.replace(/[^0-9.]/g, '');
  const target = parseFloat(raw);
  if (!isNaN(target)) countUp(el, target);
});
2. Analytics — pure CSS bar chart + KPI cards

Analytics · Last 30 days

AL

Visits

124,392
↑ 18.4% vs previous period

Top referrer

Google
54.2% of traffic

Avg session

3m 42s
↑ 14s longer
<div class="dash-layout">
  <aside class="side">
    <div class="brand">moonbase</div>
    <a>Dashboard</a>
    <a class="active">Analytics</a>
    <a>Projects</a>
  </aside>

  <div>
    <header class="topbar">
      <h2>Analytics · Last 30 days</h2>
      <div class="right"><div class="avatar">AL</div></div>
    </header>
    <div class="main">
<div class="chart-card">
  <h3>Visits</h3>
  <div class="big">124,392</div>
  <small>↑ 18.4% vs previous period</small>
  <div class="bars">
    <div class="bar" style="height:40%"></div>
    <div class="bar" style="height:60%"></div>
    <div class="bar" style="height:45%"></div>
    <div class="bar" style="height:80%"></div>
    <div class="bar" style="height:70%"></div>
    <div class="bar" style="height:92%"></div>
    <div class="bar" style="height:65%"></div>
    <div class="bar" style="height:85%"></div>
    <div class="bar" style="height:78%"></div>
    <div class="bar" style="height:100%"></div>
  </div>
</div>
<div class="row">
  <div class="chart-card"><h3>Top referrer</h3><div class="big">Google</div><small>54.2% of traffic</small></div>
  <div class="chart-card"><h3>Avg session</h3><div class="big">3m 42s</div><small>↑ 14s longer</small></div>
</div>
    </div>
  </div>
</div>
/* Pure-CSS bar chart — each .bar is a gradient rectangle */
.bars {
  display: flex;
  align-items: end;
  gap: 10px;
  height: 120px;
}

.bar {
  flex: 1;
  border-radius: 6px 6px 0 0;
  background: linear-gradient(180deg, #ec4899, #8b5cf6);
}

/* Height is set inline via style — one line of JS to update */
// Animate the bars from 0 to their target height when they scroll into view.
const bars = document.querySelectorAll('.bars .bar');

// 1) Remember each bar's target height, then collapse it to 0.
bars.forEach(bar => {
  bar.dataset.targetHeight = bar.style.height;
  bar.style.height = '0%';
  bar.style.transition = 'height .8s cubic-bezier(.2,.7,.3,1)';
});

// 2) When the chart enters the viewport, restore each bar's height.
const observer = new IntersectionObserver(entries => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      bars.forEach((bar, i) => {
        setTimeout(() => {
          bar.style.height = bar.dataset.targetHeight;
        }, i * 60);
      });
      observer.disconnect();
    }
  });
}, { threshold: 0.3 });

const chart = document.querySelector('.bars');
if (chart) observer.observe(chart);
3. Kanban Board — 3 columns of draggable task cards

Sprint 24 · Board

AL

To Do 3

Design
Onboarding wireframes v2
Priya · Due Mar 20
Dev
API rate-limit refactor
Kenji · Due Mar 21
Bug
Auth redirect loop on Safari
Marco · Urgent

In Progress 2

Design
Settings page redesign
Priya
Dev
Billing webhook integration
Marco

Done 4

Dev
Dark mode toggle
Marco ✓
Design
Logo refresh
Priya ✓
Dev
Drag & drop file upload
Kenji ✓
<div class="dash-layout">
  <aside class="side">
    <div class="brand">taskflow</div>
    <a>Home</a>
    <a class="active">Board</a>
    <a>Team</a>
  </aside>

  <div>
    <header class="topbar">
      <h2>Sprint 24 · Board</h2>
      <div class="right"><div class="avatar">AL</div></div>
    </header>
    <div class="main">
<div class="board">
  <div class="col">
    <h4>To Do <span class="count">3</span></h4>
    <div class="card"><span class="tag design">Design</span><div>Onboarding wireframes v2</div><div class="who">Priya · Due Mar 20</div></div>
    <div class="card"><span class="tag dev">Dev</span><div>API rate-limit refactor</div><div class="who">Kenji · Due Mar 21</div></div>
    <div class="card"><span class="tag bug">Bug</span><div>Auth redirect loop on Safari</div><div class="who">Marco · Urgent</div></div>
  </div>
  <div class="col">
    <h4>In Progress <span class="count">2</span></h4>
    <div class="card"><span class="tag design">Design</span><div>Settings page redesign</div><div class="who">Priya</div></div>
    <div class="card"><span class="tag dev">Dev</span><div>Billing webhook integration</div><div class="who">Marco</div></div>
  </div>
  <div class="col">
    <h4>Done <span class="count">4</span></h4>
    <div class="card"><span class="tag dev">Dev</span><div>Dark mode toggle</div><div class="who">Marco ✓</div></div>
    <div class="card"><span class="tag design">Design</span><div>Logo refresh</div><div class="who">Priya ✓</div></div>
    <div class="card"><span class="tag dev">Dev</span><div>Drag & drop file upload</div><div class="who">Kenji ✓</div></div>
  </div>
</div>
    </div>
  </div>
</div>
.board {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
}

.tag {
  padding: 2px 8px;
  border-radius: 4px;
  font-size: 10px;
  font-weight: 700;
}

.tag.design { background: #fce7f3; color: #be185d; }
.tag.dev    { background: #dbeafe; color: #1e40af; }
.tag.bug    { background: #fee2e2; color: #991b1b; }
4. Inbox — message list + preview pane (Gmail/Superhuman style)

Inbox (12)

AL
Priya Patel2m ago
Re: Design review Friday
Hey Ada — I'd love to include the new onboarding...
Stripe1h ago
Invoice #4821 is paid
Successful payment of $299.00...
Marco R.Mon
Bug fix shipped
Just merged the Safari redirect fix...
GitHubMon
PR #847 approved
Your pull request was approved by...
From Priya Patel · March 14, 2026

Re: Design review Friday

Hey Ada — I'd love to include the new onboarding screens in Friday's review. Could you push the latest Figma file by Thursday so I can comment overnight?

Also, Marco mentioned he fixed the Safari redirect bug — should we close that ticket?

Thanks!
Priya

<div class="dash-layout">
  <aside class="side">
    <div class="brand">mailbase</div>
    <a class="active">Inbox</a>
    <a>Sent</a>
    <a>Archive</a>
  </aside>

  <div>
    <header class="topbar">
      <h2>Inbox (12)</h2>
      <div class="right"><div class="avatar">AL</div></div>
    </header>
    <div class="main">
<div class="inbox">
  <div class="inbox-list">
    <div class="msg active unread"><div class="from">Priya Patel<span>2m ago</span></div><div class="subj">Re: Design review Friday</div><div class="snip">Hey Ada — I'd love to include the new onboarding...</div></div>
    <div class="msg unread"><div class="from">Stripe<span>1h ago</span></div><div class="subj">Invoice #4821 is paid</div><div class="snip">Successful payment of $299.00...</div></div>
    <div class="msg"><div class="from">Marco R.<span>Mon</span></div><div class="subj">Bug fix shipped</div><div class="snip">Just merged the Safari redirect fix...</div></div>
    <div class="msg"><div class="from">GitHub<span>Mon</span></div><div class="subj">PR #847 approved</div><div class="snip">Your pull request was approved by...</div></div>
  </div>
  <div class="viewer">
    <div class="meta">From Priya Patel · March 14, 2026</div>
    <h2>Re: Design review Friday</h2>
    <p>Hey Ada — I'd love to include the new onboarding screens in Friday's review. Could you push the latest Figma file by Thursday so I can comment overnight?</p>
    <p>Also, Marco mentioned he fixed the Safari redirect bug — should we close that ticket?</p>
    <p>Thanks!<br>Priya</p>
  </div>
</div>
    </div>
  </div>
</div>
.inbox {
  display: grid;
  grid-template-columns: 320px 1fr;
}

/* Active message gets a colored left border */
.msg.active {
  background: #fdf2f8;
  border-left: 3px solid #ec4899;
}

/* Unread dot on the subject */
.msg.unread .from::after {
  content: "●";
  color: #ec4899;
}
5. Calendar — month grid with today highlight + event dots

March 2026

AL

March 2026

Sun
Mon
Tue
Wed
Thu
Fri
Sat
26
27
28
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
<div class="dash-layout">
  <aside class="side">
    <div class="brand">calendar</div>
    <a class="active">Month</a>
    <a>Week</a>
    <a>Day</a>
  </aside>

  <div>
    <header class="topbar">
      <h2>March 2026</h2>
      <div class="right"><div class="avatar">AL</div></div>
    </header>
    <div class="main">
<div class="cal-card">
<div class="cal-head">
  <button class="nav-btn"></button>
  <h3>March 2026</h3>
  <button class="nav-btn"></button>
</div>
<div class="days">
  <div class="h">Sun</div><div class="h">Mon</div><div class="h">Tue</div><div class="h">Wed</div><div class="h">Thu</div><div class="h">Fri</div><div class="h">Sat</div>
  <div class="day dim">26</div><div class="day dim">27</div><div class="day dim">28</div><div class="day">1</div><div class="day has-event">2</div><div class="day">3</div><div class="day">4</div>
  <div class="day">5</div><div class="day has-event">6</div><div class="day">7</div><div class="day">8</div><div class="day">9</div><div class="day">10</div><div class="day">11</div>
  <div class="day">12</div><div class="day">13</div><div class="day today has-event">14</div><div class="day has-event">15</div><div class="day">16</div><div class="day has-event">17</div><div class="day">18</div>
  <div class="day">19</div><div class="day">20</div><div class="day">21</div><div class="day">22</div><div class="day has-event">23</div><div class="day">24</div><div class="day">25</div>
  <div class="day">26</div><div class="day">27</div><div class="day">28</div><div class="day">29</div><div class="day">30</div><div class="day">31</div><div class="day dim">1</div>
</div>
</div>
    </div>
  </div>
</div>
.days {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 4px;
}

.day {
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  position: relative;
}

.day.today {
  background: #ec4899;
  color: #fff;
}

/* Event dot at the bottom of the cell */
.day.has-event::after {
  content: "";
  position: absolute;
  bottom: 3px;
  width: 4px;
  height: 4px;
  background: #ec4899;
  border-radius: 50%;
}
6. User Table — data table with avatars, status badges, and actions

Users (247)

AL
UserRoleStatusLast active
AL
Ada Lovelaceada@company.com
AdminActiveJust now
GH
Grace Hoppergrace@company.com
EditorActive2 hours ago
AT
Alan Turingalan@company.com
ViewerInvitedPending
MC
Margaret Hamiltonmargaret@company.com
EditorSuspended3 days ago
<div class="dash-layout">
  <aside class="side">
    <div class="brand">admin</div>
    <a class="active">Users</a>
    <a>Roles</a>
    <a>Settings</a>
  </aside>

  <div>
    <header class="topbar">
      <h2>Users (247)</h2>
      <div class="right"><div class="avatar">AL</div></div>
    </header>
    <div class="main">
<div class="table-card">
<table>
  <thead><tr>
    <th>User</th><th>Role</th><th>Status</th><th>Last active</th>
  </tr></thead>
  <tbody>
    <tr><td><div class="user"><div class="avatar">AL</div><div><strong>Ada Lovelace</strong><small>ada@company.com</small></div></div></td><td>Admin</td><td><span class="badge active">Active</span></td><td>Just now</td></tr>
    <tr><td><div class="user"><div class="avatar" style="background:linear-gradient(135deg,#10b981,#06b6d4);">GH</div><div><strong>Grace Hopper</strong><small>grace@company.com</small></div></div></td><td>Editor</td><td><span class="badge active">Active</span></td><td>2 hours ago</td></tr>
    <tr><td><div class="user"><div class="avatar" style="background:linear-gradient(135deg,#f59e0b,#ef4444);">AT</div><div><strong>Alan Turing</strong><small>alan@company.com</small></div></div></td><td>Viewer</td><td><span class="badge invited">Invited</span></td><td>Pending</td></tr>
    <tr><td><div class="user"><div class="avatar" style="background:linear-gradient(135deg,#8b5cf6,#3b82f6);">MC</div><div><strong>Margaret Hamilton</strong><small>margaret@company.com</small></div></div></td><td>Editor</td><td><span class="badge suspended">Suspended</span></td><td>3 days ago</td></tr>
  </tbody>
</table>
</div>
    </div>
  </div>
</div>
table { width: 100%; border-collapse: collapse; }

th {
  text-align: left;
  background: #fafafa;
  text-transform: uppercase;
  letter-spacing: .08em;
  font-size: 12px;
}

.badge {
  padding: 2px 10px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
}
.badge.active    { background: #dcfce7; color: #166534; }
.badge.invited   { background: #fef3c7; color: #854d0e; }
.badge.suspended { background: #fee2e2; color: #991b1b; }
7. Settings Page — vertical tabs + form fields + toggles

Settings

AL

Profile

Your public profile and display preferences.

Show profile publiclyLet anyone find your profile by username
Weekly digest emailA summary of your activity every Monday
Marketing updatesProduct news, tips, and launches
<div class="dash-layout">
  <aside class="side">
    <div class="brand">moonbase</div>
    <a>Home</a>
    <a class="active">Settings</a>
  </aside>

  <div>
    <header class="topbar">
      <h2>Settings</h2>
      <div class="right"><div class="avatar">AL</div></div>
    </header>
    <div class="main">
<div class="settings">
  <nav>
    <button class="active">Profile</button>
    <button>Security</button>
    <button>Notifications</button>
    <button>Billing</button>
    <button>Team</button>
  </nav>
  <div class="panel">
    <h3>Profile</h3>
    <p>Your public profile and display preferences.</p>
    <div class="field"><label>Display name</label><input type="text" value="Ada Lovelace"></div>
    <div class="field"><label>Email</label><input type="email" value="ada@company.com"></div>
    <div class="toggle-row">
      <div><strong>Show profile publicly</strong><small>Let anyone find your profile by username</small></div>
      <div class="switch on"></div>
    </div>
    <div class="toggle-row">
      <div><strong>Weekly digest email</strong><small>A summary of your activity every Monday</small></div>
      <div class="switch on"></div>
    </div>
    <div class="toggle-row">
      <div><strong>Marketing updates</strong><small>Product news, tips, and launches</small></div>
      <div class="switch"></div>
    </div>
  </div>
</div>
    </div>
  </div>
</div>
.settings {
  display: grid;
  grid-template-columns: 200px 1fr;
  gap: 22px;
}

.switch {
  position: relative;
  width: 40px;
  height: 22px;
  background: #d4d4d8;
  border-radius: 999px;
}

.switch.on { background: #10b981; }

.switch::after {
  content: "";
  position: absolute;
  width: 18px;
  height: 18px;
  background: #fff;
  border-radius: 50%;
  top: 2px;
  left: 2px;
  transition: transform .2s;
}
.switch.on::after { transform: translateX(18px); }
8 · Full Page — the whole dashboard as one copy-paste file

Everything above — sidebar, top bar, stat cards, and a pure-CSS chart — combined into one complete, standalone HTML file. No external stylesheet, no build step. Here's the live preview, then the full code rendered straight from the box below.

Live preview

Complete HTML — copy this whole file

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Admin Dashboard</title>
  <style>
    * { box-sizing: border-box; margin: 0; padding: 0; }
    body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; color: #111; background: #f8fafc; }

    /* ===== Dashboard shell: sidebar + content ===== */
    .dash-layout { display: grid; grid-template-columns: 220px 1fr; min-height: 100vh; }

    .side {
      background: #0f172a; color: #cbd5e1; padding: 20px 16px;
      display: flex; flex-direction: column; gap: 4px;
    }
    .side .brand { color: #fff; font-weight: 800; padding: 0 10px 18px; border-bottom: 1px solid rgba(255,255,255,.06); margin-bottom: 10px; }
    .side a {
      display: flex; align-items: center; gap: 12px;
      padding: 9px 12px; border-radius: 8px;
      color: #cbd5e1; text-decoration: none; font-size: 13px; cursor: pointer;
    }
    .side a:hover { background: rgba(255,255,255,.05); color: #fff; }
    .side a.active { background: rgba(236,72,153,.15); color: #fbcfe8; }

    /* ===== Top bar ===== */
    .topbar {
      display: flex; align-items: center; justify-content: space-between;
      padding: 14px 24px; background: #fff; border-bottom: 1px solid #eee;
    }
    .topbar h2 { font-size: 1.1rem; }
    .topbar .right { display: flex; align-items: center; gap: 12px; }
    .avatar {
      width: 32px; height: 32px; border-radius: 50%;
      background: linear-gradient(135deg, #ec4899, #8b5cf6);
      color: #fff; display: flex; align-items: center; justify-content: center;
      font-weight: 700; font-size: 12px;
    }

    .main { padding: 24px; }

    /* ===== Stat cards ===== */
    .stats {
      display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
      gap: 14px; margin-bottom: 18px;
    }
    .stat { background: #fff; padding: 18px; border-radius: 12px; border: 1px solid #eee; }
    .stat .label { font-size: 12px; color: #64748b; text-transform: uppercase; letter-spacing: .08em; }
    .stat .value { font-size: 1.8rem; font-weight: 800; margin-top: 4px; }
    .stat .delta { font-size: 12px; margin-top: 4px; }
    .stat .delta.up { color: #10b981; }
    .stat .delta.down { color: #ef4444; }

    /* ===== Pure-CSS bar chart ===== */
    .chart-card { background: #fff; padding: 20px; border-radius: 12px; border: 1px solid #eee; }
    .chart-card h3 { font-size: 14px; margin-bottom: 2px; }
    .chart-card .big { font-size: 1.8rem; font-weight: 800; }
    .chart-card small { color: #10b981; font-size: 12px; }
    .bars { display: flex; align-items: end; gap: 10px; height: 120px; margin-top: 16px; }
    .bars .bar {
      flex: 1; border-radius: 6px 6px 0 0;
      background: linear-gradient(180deg, #ec4899, #8b5cf6);
      transition: transform .2s;
    }
    .bars .bar:hover { transform: scaleY(1.03); }

    @media (max-width: 640px) {
      .dash-layout { grid-template-columns: 1fr; }
      .side { flex-direction: row; flex-wrap: wrap; }
    }
  </style>
</head>
<body>

  <div class="dash-layout">

    <!-- Sidebar -->
    <aside class="side">
      <div class="brand">moonbase</div>
      <a class="active">&#9737; Dashboard</a>
      <a>&#9783; Team</a>
      <a>&#9783; Projects</a>
      <a>&#9881; Settings</a>
    </aside>

    <!-- Content column -->
    <div>
      <header class="topbar">
        <h2>Dashboard</h2>
        <div class="right">&#128276;<div class="avatar">AL</div></div>
      </header>

      <div class="main">
        <div class="stats">
          <div class="stat"><div class="label">Users</div><div class="value">8,247</div><div class="delta up">&#8593; 12.4% this month</div></div>
          <div class="stat"><div class="label">Revenue</div><div class="value">$42.8k</div><div class="delta up">&#8593; 8.1%</div></div>
          <div class="stat"><div class="label">Orders</div><div class="value">1,128</div><div class="delta down">&#8595; 3.2%</div></div>
          <div class="stat"><div class="label">Conversion</div><div class="value">4.72%</div><div class="delta up">&#8593; 21%</div></div>
        </div>

        <div class="chart-card">
          <h3>Visits</h3>
          <div class="big">124,392</div>
          <small>&#8593; 18.4% vs previous period</small>
          <div class="bars">
            <div class="bar" style="height:40%"></div>
            <div class="bar" style="height:60%"></div>
            <div class="bar" style="height:45%"></div>
            <div class="bar" style="height:80%"></div>
            <div class="bar" style="height:70%"></div>
            <div class="bar" style="height:92%"></div>
            <div class="bar" style="height:65%"></div>
            <div class="bar" style="height:85%"></div>
            <div class="bar" style="height:78%"></div>
            <div class="bar" style="height:100%"></div>
          </div>
        </div>
      </div>
    </div>
  </div>

  <script>
    // Sidebar links: clicking one moves the active highlight
    document.querySelectorAll('.side a').forEach(function (link) {
      link.addEventListener('click', function () {
        document.querySelectorAll('.side a').forEach(function (a) { a.classList.remove('active'); });
        link.classList.add('active');
      });
    });
  </script>
</body>
</html>