Merges workspace.html + ralph.html into a single unified project hub with: - Cookie-based auth (DASHBOARD_TOKEN, HttpOnly, SameSite=Strict) - 9-state project badge system (running-ralph/manual, planning, approved, pending, blocked, failed, complete, idle) with BUTTONS_FOR_STATE matrix - SSE realtime + polling fallback, version-based optimistic concurrency (If-Match) - Planning chat modal (phase stepper, markdown bubbles, 50s+ wait state, auto-resume) - Propose modal (Variant B: inline Plan-with-Echo checkbox) - 5-type toast taxonomy (success/info/warning/busy/error, 3px colored left-bar) - Inter font self-hosted + shared tokens.css design system + DESIGN.md - src/jsonlock.py (flock helper, sidecar .lock for stable inode) - src/approved_tasks_cli.py (shell-safe wrapper for cron/ralph.sh) - 55 new tests (T#1–T#30) + real jsonlock bug fix caught by T#16/T#28 - No emoji anywhere (enforced by test_dashboard_no_emoji.py) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
160 lines
5.0 KiB
CSS
160 lines
5.0 KiB
CSS
/*
|
|
* Echo Dashboard — Design Tokens
|
|
* Single source of truth for all CSS variables, fonts, and shared
|
|
* mobile-modal behavior. Loaded via /echo/static/tokens.css on every
|
|
* dashboard page (in addition to common.css for now).
|
|
*
|
|
* Token coverage:
|
|
* - Colors (dark default + light theme override)
|
|
* - Status palette (running, blocked, failed, complete, idle,
|
|
* planning, pending, approved)
|
|
* - Typography (Inter sans + JetBrains Mono mono, size scale)
|
|
* - Spacing (8px grid)
|
|
* - Radius scale
|
|
* - Shadows / transitions
|
|
*/
|
|
|
|
/* ==========================================================
|
|
@font-face — Inter (self-hosted, woff2 only)
|
|
========================================================== */
|
|
@font-face {
|
|
font-family: 'Inter';
|
|
src: url('/echo/static/fonts/inter-400.woff2') format('woff2');
|
|
font-weight: 400;
|
|
font-style: normal;
|
|
font-display: swap;
|
|
}
|
|
|
|
@font-face {
|
|
font-family: 'Inter';
|
|
src: url('/echo/static/fonts/inter-500.woff2') format('woff2');
|
|
font-weight: 500;
|
|
font-style: normal;
|
|
font-display: swap;
|
|
}
|
|
|
|
@font-face {
|
|
font-family: 'Inter';
|
|
src: url('/echo/static/fonts/inter-600.woff2') format('woff2');
|
|
font-weight: 600;
|
|
font-style: normal;
|
|
font-display: swap;
|
|
}
|
|
|
|
@font-face {
|
|
font-family: 'Inter';
|
|
src: url('/echo/static/fonts/inter-700.woff2') format('woff2');
|
|
font-weight: 700;
|
|
font-style: normal;
|
|
font-display: swap;
|
|
}
|
|
|
|
/* ==========================================================
|
|
Tokens — dark theme (default)
|
|
========================================================== */
|
|
:root {
|
|
/* Colors — dark surface */
|
|
--bg-base: #13131a;
|
|
--bg-surface: rgba(255, 255, 255, 0.12);
|
|
--bg-surface-hover: rgba(255, 255, 255, 0.16);
|
|
--bg-surface-active: rgba(255, 255, 255, 0.20);
|
|
--bg-elevated: rgba(255, 255, 255, 0.14);
|
|
|
|
--text-primary: #ffffff;
|
|
--text-secondary: #f5f5f5;
|
|
--text-muted: #e5e5e5;
|
|
|
|
--accent: #3b82f6;
|
|
--accent-hover: #2563eb;
|
|
--accent-subtle: rgba(59, 130, 246, 0.2);
|
|
|
|
--border: rgba(255, 255, 255, 0.3);
|
|
--border-focus: rgba(59, 130, 246, 0.7);
|
|
|
|
--header-bg: rgba(19, 19, 26, 0.95);
|
|
|
|
--success: #22c55e;
|
|
--warning: #eab308;
|
|
--error: #ef4444;
|
|
|
|
/* Status palette — used by .status-pill[data-status] */
|
|
--status-running: rgb(34, 197, 94); /* green — running-ralph / running-manual */
|
|
--status-blocked: rgb(245, 158, 11); /* amber */
|
|
--status-failed: rgb(239, 68, 68); /* red */
|
|
--status-complete: rgb(156, 163, 175); /* slate (done = neutral) */
|
|
--status-idle: var(--text-muted);
|
|
|
|
/* Status palette — extended (workflow states) */
|
|
--status-planning: rgb(167, 139, 250); /* violet — Echo is planning */
|
|
--status-pending: rgb(96, 165, 250); /* sky — awaiting approval */
|
|
--status-approved: rgb(234, 179, 8); /* gold — approved tonight */
|
|
|
|
/* Spacing — 8px grid */
|
|
--space-1: 4px;
|
|
--space-2: 8px;
|
|
--space-3: 12px;
|
|
--space-4: 16px;
|
|
--space-5: 20px;
|
|
--space-6: 24px;
|
|
--space-8: 32px;
|
|
--space-10: 40px;
|
|
|
|
/* Typography */
|
|
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
--font-mono: 'JetBrains Mono', 'Fira Code', ui-monospace, monospace;
|
|
|
|
--text-xs: 0.75rem;
|
|
--text-sm: 0.875rem;
|
|
--text-base: 1rem;
|
|
--text-lg: 1.125rem;
|
|
--text-xl: 1.25rem;
|
|
|
|
/* Radius */
|
|
--radius-sm: 4px;
|
|
--radius-md: 8px;
|
|
--radius-lg: 12px;
|
|
--radius-full: 9999px;
|
|
|
|
/* Shadows */
|
|
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
|
|
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.4);
|
|
--shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.5);
|
|
|
|
/* Motion */
|
|
--transition-fast: 0.15s ease;
|
|
--transition-base: 0.2s ease;
|
|
}
|
|
|
|
/* ==========================================================
|
|
Light theme override
|
|
========================================================== */
|
|
[data-theme="light"] {
|
|
--bg-base: #f8f9fa;
|
|
--bg-surface: rgba(0, 0, 0, 0.04);
|
|
--bg-surface-hover: rgba(0, 0, 0, 0.08);
|
|
--bg-surface-active: rgba(0, 0, 0, 0.12);
|
|
--bg-elevated: rgba(0, 0, 0, 0.06);
|
|
|
|
--text-primary: #1a1a1a;
|
|
--text-secondary: #444444;
|
|
--text-muted: #666666;
|
|
|
|
--border: rgba(0, 0, 0, 0.12);
|
|
--border-focus: rgba(59, 130, 246, 0.5);
|
|
|
|
--accent-subtle: rgba(59, 130, 246, 0.12);
|
|
|
|
--header-bg: rgba(255, 255, 255, 0.95);
|
|
}
|
|
|
|
/* ==========================================================
|
|
Mobile modal — shared across all pages with .modal-overlay
|
|
========================================================== */
|
|
@media (max-width: 640px) {
|
|
.modal-overlay { padding: 0; align-items: stretch; }
|
|
.modal { max-width: 100vw !important; max-height: 100vh !important; border-radius: 0; height: 100vh; }
|
|
.modal-header { position: sticky; top: 0; background: var(--bg-base); }
|
|
.modal-footer { position: sticky; bottom: 0; padding-bottom: max(var(--space-4), env(safe-area-inset-bottom)); }
|
|
.phase-stepper .phase-step:not(.active) span:not(.step-num) { display: none; }
|
|
}
|