Dashboard Redesign — App Shell Implementation Plan
For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal: Transform the dashboard from a card grid into an app-shell experience where the existing sidebar gains a waitlist list section, and the main content area becomes a detail panel for the selected waitlist with KPIs, activity feed, and quick actions.
Architecture: The app.html layout already provides a sidebar, topbar, theme toggle, and mobile drawer. The dashboard page content (dashboard.html) renders inside <main class="app-main">. This plan adds a dynamic waitlist list section to the sidebar (populated by dashboard.js when on the dashboard page), replaces the main content with a detail panel, and adds CSS for new components. No backend changes needed.
Tech Stack: Vanilla JS (no framework), CSS custom properties (existing theme system), existing app.js utilities (buildStatCardV2, showEl, hideEl, escapeHtml, apiCall).
Design doc: docs/plans/2026-03-09-dashboard-redesign-design.md
Security note: All dynamic content uses textContent or escapeHtml() — no raw user data is inserted via innerHTML. Only static SVG markup and escaped IDs use innerHTML. This is consistent with the existing codebase patterns.
Task 1: Add Sidebar Waitlist List Section to app.html
Files:
- Modify:
docs/_layouts/app.html:89-133(inside<aside class="app-sidebar">)
Step 1: Add waitlist list placeholder to sidebar
Add a new section at the top of .app-sidebar-nav (before the first <div class="app-sidebar-section">), with an empty container that dashboard.js will populate:
<div class="app-sidebar-section app-sidebar-waitlists" id="sidebar-waitlists-section" style="display:none;">
<div class="app-sidebar-heading">Waitlists</div>
<div id="sidebar-waitlists-list"></div>
<button class="app-sidebar-create-btn" id="sidebar-create-btn" title="Create waitlist">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
<span class="app-sidebar-label">Create Waitlist</span>
</button>
</div>
This section starts hidden (style="display:none;") and is shown by dashboard.js after loading waitlists. On non-dashboard pages it stays hidden.
Step 2: Verify the layout loads correctly
Run: cd /Users/nonplus/Desktop/makeemwait/docs && bundle exec jekyll build 2>&1 | tail -5
Expected: No Liquid errors, clean build.
Step 3: Commit
git add docs/_layouts/app.html
git commit -m "Sidebar: add waitlist list placeholder section"
Task 2: CSS for Sidebar Waitlist Items
Files:
- Modify:
docs/assets/css/style.css(add after existing.app-sidebar-badgeblock, around line 7180)
Step 1: Add sidebar waitlist item styles
Add the following CSS after the .app-sidebar-badge section (see plan for full CSS block):
Key classes to add:
.app-sidebar-waitlists— scrollable section with border-bottom, max-height: 40vh.app-sidebar-wl-item— flex row with gap, padding, rounded corners, cursor pointer, hover state.app-sidebar-wl-item.is-selected— accent-glow background with 3px left accent bar via::beforepseudo-element.app-sidebar-wl-dot— 8px circle with inline background color.app-sidebar-wl-name— flex:1, truncated with ellipsis.app-sidebar-wl-count— small muted text, tabular-nums.app-sidebar-create-btn— dashed border button, hover turns accent-colored- Collapsed sidebar variants that hide names/counts
Step 2: Verify build
Run: cd /Users/nonplus/Desktop/makeemwait/docs && bundle exec jekyll build 2>&1 | tail -5
Step 3: Commit
git add docs/assets/css/style.css
git commit -m "CSS: sidebar waitlist list items, selection, create button"
Task 3: CSS for Dashboard Detail Panel, Activity Feed, and Quick Actions
Files:
- Modify:
docs/assets/css/style.css(add after sidebar waitlist styles from Task 2)
Step 1: Add detail panel, activity feed, and micro-interaction styles
Key CSS classes to add:
.dash-detail-header— flex row with title + kebab menu.dash-detail-title— 1.35rem bold with -0.02em letter-spacing.dash-status-pill— inline-flex pill with colored dot + text, variants for.status-open(green) and.status-closed(amber), with:root:not(.dark)light-mode overrides.dash-quick-actions— flex row with gap, buttons flex:1.dash-activity— border-top section.dash-activity-item— flex row with dot + body, stagger animation via@keyframes activity-enter(opacity 0→1, translateY 8px→0) with nth-child delays (40ms increments).dash-activity-dot— 8px accent circle,.referralvariant in green.dash-overview-greeting— 1.35rem bold heading.dash-leaderboard-item— flex row with rank, name, count.dash-checklist— vertical stack of checklist items with circle checkboxes.btn:active—transform: scale(0.97)for button press feelfont-variant-numeric: tabular-numson.stat-card-v2-value@media (prefers-reduced-motion: reduce)— disables animations@media (max-width: 640px)— stacks quick actions vertically
Step 2: Verify build
Step 3: Commit
git add docs/assets/css/style.css
git commit -m "CSS: detail panel, activity feed, quick actions, checklist, micro-interactions"
Task 4: Add Animated Counter Utility to app.js
Files:
- Modify:
docs/assets/js/app.js(add afterbuildSectionHeaderfunction around line 533, beforeshowEl)
Step 1: Add animateCounter function
function animateCounter(el, target, duration, suffix) {
if (!el || typeof target !== "number") return;
duration = duration || 600;
suffix = suffix || "";
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
el.textContent = target.toLocaleString() + suffix;
return;
}
var start = performance.now();
function step(now) {
var elapsed = now - start;
var progress = Math.min(elapsed / duration, 1);
var eased = 1 - Math.pow(1 - progress, 3);
var current = Math.round(eased * target);
el.textContent = current.toLocaleString() + suffix;
if (progress < 1) requestAnimationFrame(step);
}
requestAnimationFrame(step);
}
Key points: Respects prefers-reduced-motion. Ease-out cubic curve. Uses requestAnimationFrame for smooth 60fps animation. Handles both integers and decimals via toLocaleString().
Step 2: Commit
git add docs/assets/js/app.js
git commit -m "Add animateCounter utility for stat card number animation"
Task 5: Restructure dashboard.html for Detail Panel Layout
Files:
- Modify:
docs/dashboard.html(full content rewrite between front matter---blocks)
Step 1: Replace the main content area
Key changes from the original:
- Remove
dashboard-headerwith title/subtitle/docs links/action buttons (sidebar handles waitlist listing and create button now) - Remove
stat-card-v2-gridas a static top-level element (moved into JS-rendered views) - Remove
waitlists-containerwith card grid and skeleton cards (replaced bydashboard-contentwhich JS populates with overview or detail view) - Replace inline
onclick="this.closest(...)dismiss withid="dismiss-welcome-btn"for JS event listener - Add a single
<div id="dashboard-content">that contains skeleton loading placeholders (replaced by JS) - Preserve all modals identically: share-modal, waitlist-modal (with all 5 tabs), preset-modal, blast-modal — no changes to any modal content
The HTML structure becomes:
<section class="dashboard">
<div id="welcome-banner" ...> <!-- welcome banner, unchanged -->
<div id="dashboard-content"> <!-- NEW: JS renders overview or detail here -->
<!-- skeleton placeholders -->
</div>
<!-- All modals preserved identically -->
</section>
Step 2: Verify build
Step 3: Commit
git add docs/dashboard.html
git commit -m "Dashboard HTML: restructure for sidebar+detail panel layout"
Task 6: Rewrite dashboard.js — Sidebar Population + View Rendering
This is the largest task. It rewrites loadWaitlists() and showDashboardSummary() to populate the sidebar, render overview/detail/empty views, and load an activity feed.
Files:
- Modify:
docs/assets/js/dashboard.js— rewriteloadWaitlists()(lines 115-204) andshowDashboardSummary()(lines 1181-1211)
New global variables:
allWaitlists— array of all loaded waitlistsselectedWaitlistId— currently selected waitlist ID (or null)CARD_COLORS— array of 6 hex colors for sidebar dots
New functions to add:
populateSidebarWaitlists(waitlists)— creates DOM elements for each waitlist in the sidebar, wires up click handlers to callselectWaitlist()selectWaitlist(waitlistId)— updates sidebar selection, updates URL viahistory.replaceState, callsrenderDetail()renderEmptyState(container)— shows onboarding with checklist (Account created pre-checked), CTA button, 3-step flowrenderOverview(container)— shows time-based greeting, 4 aggregate KPI cards with animated counters, top performers leaderboard (top 5 by signup count)renderDetail(container, wl)— shows header with name + status pill + kebab menu, 3 KPI cards with animated counters, 4 quick action buttons, activity feed sectionloadActivityFeed(waitlistId, container)— fetchesGET /waitlists/{id}/admin/signups?limit=10, renders each signup as an activity item with anonymized email (first char + *** + @domain), action label, and relative timestamptimeAgo(dateStr)— converts ISO date to “2m ago”, “3h ago”, “5d ago” format
Updated functions:
loadWaitlists()— callspopulateSidebarWaitlists(), checks URL?wl=param for pre-selection, renders appropriate viewshowDashboardSummary()— becomes a no-op (summary is now inline in overview/detail views)
DOMContentLoaded handler updates:
- Welcome banner: check
localStorage.getItem("mew_welcome_dismissed")before showing - Animated dismiss:
opacity → 0,translateY(-8px),setTimeout(remove, 300), persist to localStorage
Security: All user-provided data (waitlist names, emails) uses textContent for rendering, never innerHTML. The kebab menu uses escaped waitlist IDs in onclick handlers via escapeHtml(), consistent with existing codebase patterns.
Step 1: Implement all new functions
Step 2: Verify build
Step 3: Commit
git add docs/assets/js/dashboard.js
git commit -m "Dashboard JS: sidebar waitlists, overview panel, detail view, activity feed"
Task 7: Verify Everything Works Together
Step 1: Run cd /Users/nonplus/Desktop/makeemwait/docs && bundle exec jekyll build 2>&1 | tail -5 — no errors
Step 2: Run cd /Users/nonplus/Desktop/makeemwait/terraform && terraform validate — “Success!”
Step 3: Check for regressions
grep -n 'waitlists-container' docs/assets/js/dashboard.js— should return 0 (old container removed)grep -n 'dashboard-content' docs/assets/js/dashboard.js— should return multiple hitsgrep -n 'renderDetail\|renderOverview\|populateSidebarWaitlists' docs/assets/js/dashboard.js— all present
Step 4: Verify modal element IDs still match
Confirm these IDs exist in both dashboard.html and dashboard.js:
share-modal, waitlist-modal, waitlist-form, blast-modal, preset-modal, welcome-banner
Step 5: Describe expected behavior at 3 viewports
- Desktop (1280px): Sidebar with waitlist list visible, main content shows overview or detail
- Tablet (768-1023px): Sidebar 64px icon rail, waitlist dots visible
- Mobile (<768px): Sidebar hidden, hamburger reveals overlay
Task 8: Push and Deploy
Step 1: git push origin main — deploys frontend via GitHub Pages
Step 2: No Terraform deploy needed — Lambda code untouched
Step 3: Verify at makeemwait.com/dashboard.html
Files Summary
| File | Changes | Scope |
|---|---|---|
docs/_layouts/app.html |
Add waitlist list placeholder to sidebar | Low |
docs/assets/css/style.css |
Sidebar waitlist items, detail panel, activity feed, quick actions, checklist, micro-interactions (~150 lines) | High |
docs/assets/js/app.js |
Add animateCounter() utility (~15 lines) |
Low |
docs/dashboard.html |
Restructure: remove card grid, add dashboard-content container, preserve all modals |
High |
docs/assets/js/dashboard.js |
Rewrite: sidebar population, overview/detail/empty views, activity feed, animated counters (~300 lines changed) | High |
Existing Utilities Reused
showEl()/hideEl()—docs/assets/js/app.js:539-546escapeHtml()—docs/assets/js/app.js:554apiCall()—docs/assets/js/app.jsbuildStatCardV2()—docs/assets/js/app.js:446toggleDropdown()/closeAllDropdowns()—docs/assets/js/dashboard.jsopenShareModal()/openWaitlistModal()/openBlastModal()— existing dashboard.js functions- Theme system — already built in
app.htmllayout with light/dark/system toggle - Sidebar collapse/mobile drawer — already built in
app.htmllayout