Created
April 15, 2026 12:16
-
-
Save bojanrajkovic/6bec3b6ddf209edb0cf64026e6a1c19d to your computer and use it in GitHub Desktop.
ATC Top Bar Layout Exploration — 3 approaches with OKLCH design system
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>ATC Top Bar Layout Exploration</title> | |
| <style> | |
| /* ATC OKLCH Design System — Radar theme (default) */ | |
| :root { | |
| --hue: 155; | |
| --bg: oklch(12% 0.063 var(--hue)); | |
| --surface: oklch(16% 0.063 var(--hue)); | |
| --surface-raised: oklch(20% 0.060 var(--hue)); | |
| --border: oklch(25% 0.055 var(--hue)); | |
| --text: oklch(85% 0.028 var(--hue)); | |
| --text-dim: oklch(72% 0.030 var(--hue)); | |
| --queued: oklch(72% 0.15 250); | |
| --running: oklch(78% 0.16 80); | |
| --success: oklch(72% 0.16 155); | |
| --failed: oklch(72% 0.17 25); | |
| --cancelled: var(--text-dim); | |
| --accent: oklch(45% 0.20 250); | |
| --font: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; | |
| --mono: 'SF Mono', 'Fira Code', 'Cascadia Code', monospace; | |
| --text-xs: 0.625rem; | |
| --text-sm: 0.75rem; | |
| --text-base: 0.9375rem; | |
| --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1); | |
| --duration-normal: 200ms; | |
| } | |
| [data-theme="warm"] { --hue: 70; } | |
| [data-theme="radar"] { --hue: 155; } | |
| [data-theme="violet"] { --hue: 280; } | |
| [data-theme="pink"] { --hue: 310; } | |
| * { margin: 0; padding: 0; box-sizing: border-box; } | |
| body { | |
| font-family: var(--font); | |
| background: oklch(8% 0.02 var(--hue)); | |
| color: var(--text); | |
| padding: 2rem; | |
| line-height: 1.5; | |
| } | |
| h1 { | |
| font-size: 1.25rem; | |
| font-weight: 700; | |
| margin-bottom: 0.5rem; | |
| color: var(--text); | |
| } | |
| h2 { | |
| font-size: var(--text-base); | |
| font-weight: 600; | |
| margin: 2rem 0 0.75rem; | |
| color: var(--text); | |
| letter-spacing: 0.02em; | |
| } | |
| .subtitle { | |
| font-size: var(--text-sm); | |
| color: var(--text-dim); | |
| margin-bottom: 2rem; | |
| } | |
| .approach-label { | |
| font-size: var(--text-xs); | |
| font-weight: 600; | |
| letter-spacing: 0.1em; | |
| text-transform: uppercase; | |
| color: var(--text-dim); | |
| margin-bottom: 0.5rem; | |
| } | |
| .approach-desc { | |
| font-size: var(--text-sm); | |
| color: var(--text-dim); | |
| margin-bottom: 1rem; | |
| max-width: 700px; | |
| } | |
| .mockup-container { | |
| background: var(--bg); | |
| border: 1px solid var(--border); | |
| border-radius: 8px; | |
| overflow: hidden; | |
| margin-bottom: 2.5rem; | |
| } | |
| /* ─── Shared TopBar Primitives ──────────────────── */ | |
| .logo { | |
| font-family: var(--mono); | |
| font-size: 1rem; | |
| font-weight: 700; | |
| color: var(--text); | |
| letter-spacing: -0.02em; | |
| white-space: nowrap; | |
| } | |
| .pool { | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| } | |
| .pool-dot { | |
| width: 8px; | |
| height: 8px; | |
| border-radius: 50%; | |
| flex-shrink: 0; | |
| } | |
| .pool-dot.green { background: var(--success); } | |
| .pool-dot.amber { background: var(--running); } | |
| .pool-dot.red { background: var(--failed); } | |
| .pool-dot.dim { background: var(--text-dim); opacity: 0.5; } | |
| .pool-label { | |
| font-family: var(--mono); | |
| font-size: var(--text-xs); | |
| color: var(--text-dim); | |
| white-space: nowrap; | |
| } | |
| .capacity-bar { | |
| height: 6px; | |
| border-radius: 3px; | |
| background: var(--surface-raised); | |
| overflow: hidden; | |
| position: relative; | |
| } | |
| .capacity-fill { | |
| height: 100%; | |
| border-radius: 3px; | |
| transition: width var(--duration-normal) var(--ease-out-expo); | |
| } | |
| .capacity-fill.green { background: var(--success); } | |
| .capacity-fill.amber { background: var(--running); } | |
| .capacity-fill.red { background: var(--failed); } | |
| .pool-count { | |
| font-family: var(--mono); | |
| font-size: var(--text-xs); | |
| color: var(--text-dim); | |
| font-variant-numeric: tabular-nums; | |
| white-space: nowrap; | |
| } | |
| .queued-badge { | |
| font-size: 0.5625rem; | |
| font-weight: 600; | |
| padding: 1px 5px; | |
| border-radius: 9999px; | |
| background: oklch(from var(--running) l c h / 0.2); | |
| color: var(--running); | |
| font-variant-numeric: tabular-nums; | |
| white-space: nowrap; | |
| } | |
| .connection-dot { | |
| width: 8px; | |
| height: 8px; | |
| border-radius: 50%; | |
| flex-shrink: 0; | |
| } | |
| .connection-dot.live { background: var(--success); box-shadow: 0 0 6px oklch(from var(--success) l c h / 0.5); } | |
| .connection-dot.stale { background: var(--running); } | |
| .connection-dot.disconnected { background: var(--failed); } | |
| .theme-dot { | |
| width: 14px; | |
| height: 14px; | |
| border-radius: 50%; | |
| border: 2px solid transparent; | |
| cursor: pointer; | |
| transition: border-color var(--duration-normal) var(--ease-out-expo); | |
| } | |
| .theme-dot:hover { border-color: var(--text-dim); } | |
| .theme-dot.active { border-color: var(--text); } | |
| .theme-dot.warm { background: oklch(50% 0.08 70); } | |
| .theme-dot.radar { background: oklch(50% 0.08 155); } | |
| .theme-dot.violet { background: oklch(50% 0.08 280); } | |
| .theme-dot.pink { background: oklch(50% 0.08 310); } | |
| .mode-toggle, .density-toggle { | |
| font-size: var(--text-xs); | |
| padding: 2px 8px; | |
| border-radius: 4px; | |
| background: var(--surface-raised); | |
| color: var(--text-dim); | |
| border: 1px solid var(--border); | |
| cursor: pointer; | |
| font-family: var(--font); | |
| transition: all var(--duration-normal) var(--ease-out-expo); | |
| } | |
| .mode-toggle:hover, .density-toggle:hover { | |
| color: var(--text); | |
| border-color: var(--text-dim); | |
| } | |
| .separator { | |
| width: 1px; | |
| background: var(--border); | |
| align-self: stretch; | |
| margin: 6px 0; | |
| } | |
| /* Content placeholder */ | |
| .content-area { | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| color: var(--text-dim); | |
| font-size: var(--text-sm); | |
| padding: 3rem; | |
| border-top: 1px solid var(--border); | |
| } | |
| /* ─── Approach A: Single-strip dense ─────────────── */ | |
| .topbar-a { | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| padding: 0 16px; | |
| height: 40px; | |
| background: var(--surface); | |
| border-bottom: 1px solid var(--border); | |
| } | |
| .topbar-a .runner-bar { | |
| display: flex; | |
| align-items: center; | |
| gap: 16px; | |
| flex: 1; | |
| margin-left: 8px; | |
| } | |
| .topbar-a .capacity-bar { | |
| width: 60px; | |
| } | |
| .topbar-a .controls { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| margin-left: auto; | |
| } | |
| .topbar-a .theme-dots { | |
| display: flex; | |
| gap: 4px; | |
| align-items: center; | |
| } | |
| /* ─── Approach B: Sectioned with popover ────────── */ | |
| .topbar-b { | |
| display: flex; | |
| align-items: center; | |
| gap: 0; | |
| padding: 0 16px; | |
| height: 48px; | |
| background: var(--surface); | |
| border-bottom: 1px solid var(--border); | |
| } | |
| .topbar-b .section { | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| padding: 0 16px; | |
| } | |
| .topbar-b .section:first-child { | |
| padding-left: 0; | |
| } | |
| .topbar-b .runner-bar { | |
| display: flex; | |
| align-items: center; | |
| gap: 20px; | |
| flex: 1; | |
| } | |
| .topbar-b .capacity-bar { | |
| width: 80px; | |
| } | |
| .topbar-b .controls { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| } | |
| .gear-btn { | |
| width: 28px; | |
| height: 28px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| border-radius: 6px; | |
| background: var(--surface-raised); | |
| border: 1px solid var(--border); | |
| color: var(--text-dim); | |
| cursor: pointer; | |
| font-size: var(--text-sm); | |
| transition: all var(--duration-normal) var(--ease-out-expo); | |
| } | |
| .gear-btn:hover { | |
| color: var(--text); | |
| border-color: var(--text-dim); | |
| } | |
| .popover { | |
| position: absolute; | |
| top: calc(100% + 6px); | |
| right: 0; | |
| background: var(--surface); | |
| border: 1px solid var(--border); | |
| border-radius: 8px; | |
| padding: 12px; | |
| min-width: 200px; | |
| box-shadow: 0 8px 24px oklch(0% 0 0 / 0.4); | |
| z-index: 10; | |
| display: none; | |
| } | |
| .popover.open { display: block; } | |
| .popover-row { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| padding: 6px 0; | |
| } | |
| .popover-label { | |
| font-size: var(--text-xs); | |
| font-weight: 600; | |
| letter-spacing: 0.05em; | |
| text-transform: uppercase; | |
| color: var(--text-dim); | |
| } | |
| .popover-divider { | |
| height: 1px; | |
| background: var(--border); | |
| margin: 6px 0; | |
| } | |
| .relative { position: relative; } | |
| /* ─── Approach C: Two-row split ─────────────────── */ | |
| .topbar-c-row1 { | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| padding: 0 16px; | |
| height: 32px; | |
| background: var(--surface); | |
| } | |
| .topbar-c-row1 .controls { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| margin-left: auto; | |
| } | |
| .topbar-c-row1 .theme-dots { | |
| display: flex; | |
| gap: 4px; | |
| align-items: center; | |
| } | |
| .topbar-c-row2 { | |
| display: flex; | |
| align-items: center; | |
| gap: 20px; | |
| padding: 0 16px; | |
| height: 32px; | |
| background: oklch(14% 0.063 var(--hue)); | |
| border-bottom: 1px solid var(--border); | |
| } | |
| .topbar-c-row2 .capacity-bar { | |
| width: 100px; | |
| } | |
| /* ─── Global Theme Switcher ─────────────────────── */ | |
| .global-controls { | |
| position: fixed; | |
| bottom: 1.5rem; | |
| right: 1.5rem; | |
| background: var(--surface); | |
| border: 1px solid var(--border); | |
| border-radius: 8px; | |
| padding: 12px 16px; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 8px; | |
| box-shadow: 0 8px 24px oklch(0% 0 0 / 0.4); | |
| z-index: 100; | |
| } | |
| .global-controls .label { | |
| font-size: var(--text-xs); | |
| font-weight: 600; | |
| letter-spacing: 0.1em; | |
| text-transform: uppercase; | |
| color: var(--text-dim); | |
| } | |
| .global-controls .theme-row { | |
| display: flex; | |
| gap: 6px; | |
| } | |
| .global-theme-dot { | |
| width: 20px; | |
| height: 20px; | |
| border-radius: 50%; | |
| border: 2px solid transparent; | |
| cursor: pointer; | |
| transition: border-color var(--duration-normal) var(--ease-out-expo); | |
| } | |
| .global-theme-dot:hover { border-color: var(--text-dim); } | |
| .global-theme-dot.active { border-color: var(--text); } | |
| .global-theme-dot.warm { background: oklch(50% 0.08 70); } | |
| .global-theme-dot.radar { background: oklch(50% 0.08 155); } | |
| .global-theme-dot.violet { background: oklch(50% 0.08 280); } | |
| .global-theme-dot.pink { background: oklch(50% 0.08 310); } | |
| </style> | |
| </head> | |
| <body data-theme="radar"> | |
| <h1>ATC Top Bar — Layout Exploration</h1> | |
| <p class="subtitle">Three approaches using the OKLCH design system. Switch themes below-right to see token remapping.</p> | |
| <!-- ─── APPROACH A ─────────────────────────────────── --> | |
| <h2>Approach A: Single-Strip Dense</h2> | |
| <p class="approach-label">~40px · Grafana / Concourse density</p> | |
| <p class="approach-desc">Everything visible at a glance. Runner pools as inline chain, theme controls always showing. Zero clicks to see any status. Most "information ruthless."</p> | |
| <div class="mockup-container"> | |
| <div class="topbar-a"> | |
| <span class="logo">ATC</span> | |
| <div class="runner-bar"> | |
| <!-- Pool 1: self-hosted, 60% (green) --> | |
| <div class="pool"> | |
| <div class="pool-dot green"></div> | |
| <span class="pool-label">ubuntu-latest</span> | |
| <div class="capacity-bar"><div class="capacity-fill green" style="width: 60%"></div></div> | |
| <span class="pool-count">3/5</span> | |
| </div> | |
| <!-- Pool 2: self-hosted, 100% (red) + queued --> | |
| <div class="pool"> | |
| <div class="pool-dot red"></div> | |
| <span class="pool-label">macos-arm64</span> | |
| <div class="capacity-bar"><div class="capacity-fill red" style="width: 100%"></div></div> | |
| <span class="pool-count">2/2</span> | |
| <span class="queued-badge">+3 queued</span> | |
| </div> | |
| <!-- Pool 3: elastic (no bar) --> | |
| <div class="pool"> | |
| <div class="pool-dot dim"></div> | |
| <span class="pool-label">gh-hosted</span> | |
| <span class="pool-count">12 running</span> | |
| </div> | |
| </div> | |
| <div class="controls"> | |
| <div class="connection-dot live" title="Connected"></div> | |
| <div class="separator"></div> | |
| <div class="theme-dots"> | |
| <div class="theme-dot warm" onclick="setTheme('warm')"></div> | |
| <div class="theme-dot radar active" onclick="setTheme('radar')"></div> | |
| <div class="theme-dot violet" onclick="setTheme('violet')"></div> | |
| <div class="theme-dot pink" onclick="setTheme('pink')"></div> | |
| </div> | |
| <button class="mode-toggle" onclick="toggleMode(this)">Dark</button> | |
| <button class="density-toggle">Cozy</button> | |
| </div> | |
| </div> | |
| <div class="content-area">Kanban board area (Sub-Phase 3)</div> | |
| </div> | |
| <!-- ─── APPROACH B ─────────────────────────────────── --> | |
| <h2>Approach B: Sectioned with Popover</h2> | |
| <p class="approach-label">~48px · Linear-inspired sections</p> | |
| <p class="approach-desc">Clear visual sections with separators. Runner pools get more breathing room with wider bars. Settings behind a gear icon popover — cleaner but one extra click.</p> | |
| <div class="mockup-container"> | |
| <div class="topbar-b"> | |
| <div class="section"> | |
| <span class="logo">ATC</span> | |
| </div> | |
| <div class="separator"></div> | |
| <div class="section runner-bar"> | |
| <div class="pool"> | |
| <div class="pool-dot green"></div> | |
| <span class="pool-label">ubuntu-latest</span> | |
| <div class="capacity-bar"><div class="capacity-fill green" style="width: 60%"></div></div> | |
| <span class="pool-count">3/5</span> | |
| </div> | |
| <div class="pool"> | |
| <div class="pool-dot red"></div> | |
| <span class="pool-label">macos-arm64</span> | |
| <div class="capacity-bar"><div class="capacity-fill red" style="width: 100%"></div></div> | |
| <span class="pool-count">2/2</span> | |
| <span class="queued-badge">+3 queued</span> | |
| </div> | |
| <div class="pool"> | |
| <div class="pool-dot dim"></div> | |
| <span class="pool-label">gh-hosted</span> | |
| <span class="pool-count">12 running</span> | |
| </div> | |
| </div> | |
| <div class="separator"></div> | |
| <div class="section controls relative"> | |
| <div class="connection-dot live" title="Connected"></div> | |
| <div class="gear-btn" onclick="togglePopover(this)">⚙</div> | |
| <div class="popover" id="popover-b"> | |
| <div class="popover-row"> | |
| <span class="popover-label">Theme</span> | |
| <div style="display:flex;gap:4px;"> | |
| <div class="theme-dot warm" onclick="setTheme('warm')"></div> | |
| <div class="theme-dot radar active" onclick="setTheme('radar')"></div> | |
| <div class="theme-dot violet" onclick="setTheme('violet')"></div> | |
| <div class="theme-dot pink" onclick="setTheme('pink')"></div> | |
| </div> | |
| </div> | |
| <div class="popover-divider"></div> | |
| <div class="popover-row"> | |
| <span class="popover-label">Mode</span> | |
| <button class="mode-toggle" onclick="toggleMode(this)">Dark</button> | |
| </div> | |
| <div class="popover-divider"></div> | |
| <div class="popover-row"> | |
| <span class="popover-label">Density</span> | |
| <button class="density-toggle">Cozy</button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="content-area">Kanban board area (Sub-Phase 3)</div> | |
| </div> | |
| <!-- ─── APPROACH C ─────────────────────────────────── --> | |
| <h2>Approach C: Two-Row Split</h2> | |
| <p class="approach-label">~64px total · Dedicated runner bar row</p> | |
| <p class="approach-desc">Row 1: branding + connection + theme controls. Row 2: full-width runner bar. Pools get maximum horizontal space. Best when runner pool count is high or variable.</p> | |
| <div class="mockup-container"> | |
| <div class="topbar-c-row1"> | |
| <span class="logo">ATC</span> | |
| <div class="controls"> | |
| <div class="connection-dot live" title="Connected"></div> | |
| <div class="separator"></div> | |
| <div class="theme-dots"> | |
| <div class="theme-dot warm" onclick="setTheme('warm')"></div> | |
| <div class="theme-dot radar active" onclick="setTheme('radar')"></div> | |
| <div class="theme-dot violet" onclick="setTheme('violet')"></div> | |
| <div class="theme-dot pink" onclick="setTheme('pink')"></div> | |
| </div> | |
| <button class="mode-toggle" onclick="toggleMode(this)">Dark</button> | |
| <button class="density-toggle">Cozy</button> | |
| </div> | |
| </div> | |
| <div class="topbar-c-row2"> | |
| <div class="pool"> | |
| <div class="pool-dot green"></div> | |
| <span class="pool-label">ubuntu-latest</span> | |
| <div class="capacity-bar"><div class="capacity-fill green" style="width: 60%"></div></div> | |
| <span class="pool-count">3/5</span> | |
| </div> | |
| <div class="pool"> | |
| <div class="pool-dot amber"></div> | |
| <span class="pool-label">ubuntu-24.04</span> | |
| <div class="capacity-bar"><div class="capacity-fill amber" style="width: 80%"></div></div> | |
| <span class="pool-count">4/5</span> | |
| <span class="queued-badge">+1 queued</span> | |
| </div> | |
| <div class="pool"> | |
| <div class="pool-dot red"></div> | |
| <span class="pool-label">macos-arm64</span> | |
| <div class="capacity-bar"><div class="capacity-fill red" style="width: 100%"></div></div> | |
| <span class="pool-count">2/2</span> | |
| <span class="queued-badge">+3 queued</span> | |
| </div> | |
| <div class="pool"> | |
| <div class="pool-dot dim"></div> | |
| <span class="pool-label">gh-hosted</span> | |
| <span class="pool-count">12 running</span> | |
| </div> | |
| <div class="pool"> | |
| <div class="pool-dot green"></div> | |
| <span class="pool-label">gpu-build</span> | |
| <div class="capacity-bar"><div class="capacity-fill green" style="width: 33%"></div></div> | |
| <span class="pool-count">1/3</span> | |
| </div> | |
| </div> | |
| <div class="content-area">Kanban board area (Sub-Phase 3)</div> | |
| </div> | |
| <!-- ─── Connection States Demo ─────────────────────── --> | |
| <h2>Connection Indicator States</h2> | |
| <p class="approach-desc">The ConnectionIndicator maps ConnectionStore states to visual treatments.</p> | |
| <div style="display: flex; gap: 2rem; margin-bottom: 2.5rem;"> | |
| <div style="display: flex; align-items: center; gap: 8px;"> | |
| <div class="connection-dot live"></div> | |
| <span style="font-size: var(--text-xs); color: var(--text-dim);">Live (connected, events flowing)</span> | |
| </div> | |
| <div style="display: flex; align-items: center; gap: 8px;"> | |
| <div class="connection-dot stale"></div> | |
| <span style="font-size: var(--text-xs); color: var(--text-dim);">Stale (connected, no events 30s+)</span> | |
| </div> | |
| <div style="display: flex; align-items: center; gap: 8px;"> | |
| <div class="connection-dot" style="background: var(--queued); opacity: 0.7;"></div> | |
| <span style="font-size: var(--text-xs); color: var(--text-dim);">Connecting / Reconnecting</span> | |
| </div> | |
| <div style="display: flex; align-items: center; gap: 8px;"> | |
| <div class="connection-dot disconnected"></div> | |
| <span style="font-size: var(--text-xs); color: var(--text-dim);">Disconnected</span> | |
| </div> | |
| </div> | |
| <!-- ─── Queue Pressure Rendering ───────────────────── --> | |
| <h2>Runner Pool Variants</h2> | |
| <p class="approach-desc">All three pool types: known capacity with thresholds, unknown capacity, and elastic. Queue pressure shown as amber badge for all types.</p> | |
| <div style="display: flex; flex-direction: column; gap: 16px; margin-bottom: 2rem; padding: 16px; background: var(--surface); border-radius: 8px; border: 1px solid var(--border);"> | |
| <!-- healthy self-hosted --> | |
| <div style="display: flex; align-items: center; gap: 12px;"> | |
| <div class="pool-dot green"></div> | |
| <span class="pool-label" style="width: 100px;">ubuntu-latest</span> | |
| <div class="capacity-bar" style="width: 120px;"><div class="capacity-fill green" style="width: 40%"></div></div> | |
| <span class="pool-count">2/5</span> | |
| <span style="font-size: var(--text-xs); color: var(--text-dim); margin-left: 1rem;">Self-hosted, green (<70%)</span> | |
| </div> | |
| <!-- warm self-hosted --> | |
| <div style="display: flex; align-items: center; gap: 12px;"> | |
| <div class="pool-dot amber"></div> | |
| <span class="pool-label" style="width: 100px;">macos-arm64</span> | |
| <div class="capacity-bar" style="width: 120px;"><div class="capacity-fill amber" style="width: 80%"></div></div> | |
| <span class="pool-count">4/5</span> | |
| <span style="font-size: var(--text-xs); color: var(--text-dim); margin-left: 1rem;">Self-hosted, amber (70-99%)</span> | |
| </div> | |
| <!-- saturated self-hosted + queue --> | |
| <div style="display: flex; align-items: center; gap: 12px;"> | |
| <div class="pool-dot red"></div> | |
| <span class="pool-label" style="width: 100px;">gpu-build</span> | |
| <div class="capacity-bar" style="width: 120px;"><div class="capacity-fill red" style="width: 100%"></div></div> | |
| <span class="pool-count">3/3</span> | |
| <span class="queued-badge">+2 queued</span> | |
| <span style="font-size: var(--text-xs); color: var(--text-dim); margin-left: 0.5rem;">Self-hosted, red (100%) + queue</span> | |
| </div> | |
| <!-- unknown capacity --> | |
| <div style="display: flex; align-items: center; gap: 12px;"> | |
| <div class="pool-dot dim"></div> | |
| <span class="pool-label" style="width: 100px;">legacy-pool</span> | |
| <span class="pool-count">5 running</span> | |
| <span class="queued-badge">+1 queued</span> | |
| <span style="font-size: var(--text-xs); color: var(--text-dim); margin-left: 0.5rem;">Unknown capacity (no bar, count + queue)</span> | |
| </div> | |
| <!-- elastic --> | |
| <div style="display: flex; align-items: center; gap: 12px;"> | |
| <div class="pool-dot dim"></div> | |
| <span class="pool-label" style="width: 100px;">gh-hosted</span> | |
| <span class="pool-count">12 running</span> | |
| <span style="font-size: var(--text-xs); color: var(--text-dim); margin-left: 1rem;">Elastic (count only, no bar)</span> | |
| </div> | |
| <!-- elastic with queue pressure --> | |
| <div style="display: flex; align-items: center; gap: 12px;"> | |
| <div class="pool-dot amber"></div> | |
| <span class="pool-label" style="width: 100px;">gh-hosted</span> | |
| <span class="pool-count">25 running</span> | |
| <span class="queued-badge">+8 queued</span> | |
| <span style="font-size: var(--text-xs); color: var(--text-dim); margin-left: 0.5rem;">Elastic with queue backup!</span> | |
| </div> | |
| </div> | |
| <!-- ─── Global Theme Switcher ─────────────────────── --> | |
| <div class="global-controls"> | |
| <span class="label">Preview Theme</span> | |
| <div class="theme-row"> | |
| <div class="global-theme-dot warm" onclick="setTheme('warm')"></div> | |
| <div class="global-theme-dot radar active" onclick="setTheme('radar')"></div> | |
| <div class="global-theme-dot violet" onclick="setTheme('violet')"></div> | |
| <div class="global-theme-dot pink" onclick="setTheme('pink')"></div> | |
| </div> | |
| </div> | |
| <script> | |
| function setTheme(theme) { | |
| document.body.setAttribute('data-theme', theme); | |
| document.querySelectorAll('.theme-dot, .global-theme-dot').forEach(d => { | |
| d.classList.toggle('active', d.classList.contains(theme)); | |
| }); | |
| } | |
| function toggleMode(btn) { | |
| const isDark = btn.textContent === 'Dark'; | |
| btn.textContent = isDark ? 'Light' : 'Dark'; | |
| // In real app, this would toggle data-mode attribute | |
| } | |
| function togglePopover(btn) { | |
| const pop = btn.nextElementSibling; | |
| pop.classList.toggle('open'); | |
| } | |
| // Close popover on outside click | |
| document.addEventListener('click', (e) => { | |
| if (!e.target.closest('.relative')) { | |
| document.querySelectorAll('.popover').forEach(p => p.classList.remove('open')); | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment