Dashboard Redesign — App Shell with Sidebar + Detail Panel
Date: 2026-03-09 Inspiration: Lemon Squeezy (playful, approachable), Linear (sidebar app shell), Vercel (micro-interactions) Approach: Sidebar app shell with selected-waitlist detail panel, dual light/dark themes
1. Overall Layout: App Shell
Transform the dashboard from a page into an app shell. Left sidebar lists all waitlists. Selecting one loads its detail panel on the right.
┌─────────────────────────────────────────────────────────────────┐
│ [Logo] MakeEmWait [?Help] [⚙Settings] [👤Avatar] │
├──────────────┬──────────────────────────────────────────────────┤
│ │ │
│ WAITLISTS │ Main content panel │
│ ────────── │ (changes based on selection) │
│ ● Beta │ │
│ ○ Launch │ │
│ ○ VIP │ │
│ │ │
│ [+ Create] │ │
│ │ │
│ ────────── │ │
│ Plan: Pro │ │
│ 6d trial │ │
│ │ │
├──────────────┤ │
│ [Analytics] │ │
│ [Templates] │ │
│ [Domains] │ │
│ [Team] │ │
│ [Account] │ │
└──────────────┴──────────────────────────────────────────────────┘
- Sidebar width: 260px desktop, collapses to 48px icon rail on tablet, slide-out drawer on mobile
- Sidebar sections: Waitlist list (top, scrollable), Plan status (middle), Navigation links (bottom)
- Each waitlist shows: colored accent dot, name (truncated), signup count
- Selected waitlist has accent-tinted background
- Mobile: hamburger icon at top-left reveals sidebar as overlay drawer with backdrop
2. Main Panel — Selected Waitlist View
When a waitlist is selected:
Header Row
- Waitlist name (h2) + status badge pill (Open = green, Closed = amber)
- Creation date in muted text
- Kebab menu (⋯) for: Export CSV, Import CSV, View Form, Delete
KPI Row (3 cards)
- Total Signups: count with sparkline and delta (“▲ +248 this week”)
- Today’s Signups: count with delta vs yesterday
- Viral Rate: percentage with delta vs last week
- Cards use
buildStatCardV2()with animated counters (count up from 0)
Quick Actions Row
- 4 buttons in a flex row: Share, Edit, Send Blast, View Analytics
- Share opens existing share modal
- Edit opens existing waitlist edit modal
- Send Blast opens existing blast modal
- View Analytics navigates to analytics.html with waitlist pre-selected
Activity Feed
- Section heading “Recent Activity”
- Fetched from existing
GET /waitlists/{id}/admin/signups?limit=10 - Each entry: relative timestamp, anonymized email (j***@acme.com), action label
- Action types: “signed up”, “signed up via referral”, “referred N people”
- Items stagger-fade in on load
- “View all signups →” link to waitlist-detail.html
3. Overview State (No Waitlist Selected)
Shown on first load when user has 1+ waitlists but none is selected:
- Greeting: “Good morning!” / “Good afternoon!” / “Good evening!” based on local time
- 4 aggregate KPI cards: Total Signups, Waitlists, Viral Rate, Plan
- “Top Performers” mini-leaderboard: top 3 waitlists by signup count with weekly delta
- Prompt: “Select a waitlist to view details, or create a new one.”
4. Empty State (Zero Waitlists)
Full-width centered empty state:
- Rocket icon (SVG)
- “Create your first waitlist” heading
- “Set up in 30 seconds. Share a link. Watch it grow.” subtext
- Primary CTA button (not outline)
- 3-step flow: Create → Share → Grow (with icons)
- Progress checklist (Zeigarnik effect):
- ✓ Account created (pre-checked)
- ○ Create first waitlist
- ○ Get your first signup
- Trial status reminder at bottom
5. Dual Theme System
Implementation
CSS custom properties scoped under [data-theme="dark"] and [data-theme="light"] on <html>.
Auto-detect on load:
var prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
document.documentElement.setAttribute("data-theme", prefersDark ? "dark" : "light");
Listen for OS theme changes:
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", function(e) {
document.documentElement.setAttribute("data-theme", e.matches ? "dark" : "light");
});
Dark Theme (refined from current)
--bg:#0a0a0a(pure near-black, no blue cast)--bg-surface:#111111--bg-card:#161618--border:#27272a(zinc-800)--border-subtle:#1e1e21--text:#e4e5e9--text-muted:#a1a1aa(zinc-400)- Cards: glass-card with
backdrop-filter: blur
Light Theme (new, Lemon Squeezy–inspired)
--bg:#fafafa--bg-surface:#ffffff--bg-card:#ffffff--border:#e4e4e7(zinc-200)--border-subtle:#f0f0f2--text:#18181b(zinc-900)--text-muted:#71717a(zinc-500)- Cards: white with
box-shadow: 0 1px 3px rgba(0,0,0,0.08)(no glass effect) --glass-bgin light:rgba(255,255,255,0.8)--glass-borderin light:rgba(0,0,0,0.06)
Shared (both themes)
--accent:#6366f1(indigo — works on both)--accent-hover:#4f46e5--success:#22c55e--danger:#ef4444--warning:#f59e0b
6. Micro-interactions & Polish
Animated Counters
When stat card values load, animate them counting up from 0 using requestAnimationFrame. Duration: 600ms with ease-out. ~15 lines of vanilla JS.
Button Press Feedback
.btn:active { transform: scale(0.97); transition: transform 80ms ease; }
Sidebar Selection
- Smooth
background-colortransition (200ms) - Selected item has
background: var(--accent-subtle)+ left accent bar (3px)
Activity Feed Entrance
Items stagger-fade in: opacity: 0 → 1 + translateY(8px) → 0 with 50ms delays.
Welcome Banner Dismiss
Animated: opacity → 0, transform: translateY(-8px), then remove after 300ms. Persists via localStorage.
prefers-reduced-motion
All animations disabled:
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
Typography
font-variant-numeric: tabular-numson all stat valuesletter-spacing: -0.02emon h1,-0.01emon h2/h3
7. Responsive Behavior
Desktop (≥1024px)
- Sidebar: 260px fixed
- Main panel: fills remaining width
Tablet (768px–1023px)
- Sidebar: 48px icon rail (waitlist dots + nav icons, no text)
- Hover/tap on sidebar item shows tooltip with waitlist name
- Main panel: fills remaining width
Mobile (< 768px)
- Sidebar: hidden by default
- Hamburger button in top-left corner
- Sidebar slides in as overlay with semi-transparent backdrop
- Tap backdrop or select item to dismiss
- Main panel: full width
8. Backend Changes
None. The activity feed uses the existing GET /waitlists/{id}/admin/signups?limit=10 endpoint. Overview KPIs use the existing GET /waitlists response data. No new endpoints needed.
9. Files Impacted
| File | Changes | Scope |
|---|---|---|
docs/assets/css/style.css |
Light theme vars, sidebar layout, detail panel, activity feed, responsive breakpoints | High |
docs/dashboard.html |
Full restructure to sidebar + main panel layout | High |
docs/assets/js/dashboard.js |
Sidebar selection state, activity feed, animated counters, overview/detail views | High |
docs/assets/js/app.js |
Theme detection/toggle, animated counter utility, theme listener | Low |
10. Design Principles
- App shell, not page — the sidebar persists, content changes. Feels like an app.
- Data near actions — KPIs and action buttons live next to each other for the selected waitlist
- Activity gives life — the feed makes the dashboard feel alive, not static
- Dual themes as a signal of quality — respecting OS preference feels premium
- Progressive disclosure — overview → select waitlist → detail → deep analytics
- Animated numbers tell stories — counting up creates a moment of delight
- Accessible by default — reduced motion, focus-visible, proper contrast ratios