Files
flynn/docs/plans/2026-02-18-gateway-ui-redesign.md
T
William Valentin 7480c67338 docs: add gateway UI redesign implementation plan
10-task plan covering Tailwind migration, responsive mobile nav,
and page-by-page rewrites with Inter font and modern dark theme.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 12:45:12 -08:00

17 KiB

Gateway UI Responsive Redesign — Implementation Plan

For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

Goal: Redesign the Flynn gateway web frontend with Tailwind CSS, modern sans-serif typography, and swipeable mobile navigation optimized for dashboard monitoring.

Architecture: Replace the existing 1740-line style.css with Tailwind utility classes applied directly in JS page renderers. Add CSS scroll-snap horizontal navigation for mobile (<768px). Desktop keeps a refined sidebar. All changes are in src/gateway/ui/ — vanilla JS, no build step.

Tech Stack: Tailwind CSS (CDN), Inter font (Google Fonts), vanilla JS ES modules.


Task 1: Update HTML Shell and Add Dependencies

Files:

  • Modify: src/gateway/ui/index.html
  • Modify: src/gateway/ui/manifest.webmanifest

Step 1: Rewrite index.html with Tailwind CDN, fonts, and new shell structure

Replace the entire file:

<!DOCTYPE html>
<html lang="en" class="dark">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="theme-color" content="#09090b">
  <title>Flynn</title>
  <link rel="manifest" href="manifest.webmanifest">
  <link rel="icon" type="image/svg+xml" href="flynn-icon.svg">
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
  <script src="https://cdn.tailwindcss.com"></script>
  <script>
  tailwind.config = {
    darkMode: 'class',
    theme: {
      extend: {
        fontFamily: {
          sans: ['Inter', 'system-ui', 'sans-serif'],
          mono: ['"JetBrains Mono"', '"Fira Code"', 'monospace'],
        },
        colors: {
          zinc: {
            950: '#09090b',
          }
        }
      }
    }
  }
  </script>
  <link rel="stylesheet" href="style.css">
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
</head>
<body class="bg-zinc-950 text-zinc-50 font-sans antialiased h-screen w-screen overflow-hidden">
  <!-- Desktop: sidebar + content. Mobile: top bar + swipe pages -->
  <div id="app-shell" class="flex h-screen overflow-hidden">
    <!-- Sidebar (desktop/tablet only) -->
    <nav id="sidebar" class="hidden md:flex w-16 lg:w-56 flex-col bg-zinc-900 border-r border-zinc-800 shrink-0 transition-all">
      <div class="p-4 border-b border-zinc-800 flex items-center gap-2">
        <span class="text-blue-500 font-bold text-lg">Flynn</span>
      </div>
      <div class="flex-1 py-2">
        <a href="#/" class="nav-link flex items-center gap-3 px-4 py-2.5 text-zinc-400 hover:text-zinc-50 hover:bg-zinc-800 transition-colors border-l-2 border-transparent" data-page="dashboard">
          <span class="text-base w-5 text-center shrink-0">&#9632;</span>
          <span class="hidden lg:inline text-sm">Dashboard</span>
        </a>
        <a href="#/chat" class="nav-link flex items-center gap-3 px-4 py-2.5 text-zinc-400 hover:text-zinc-50 hover:bg-zinc-800 transition-colors border-l-2 border-transparent" data-page="chat">
          <span class="text-base w-5 text-center shrink-0">&#9993;</span>
          <span class="hidden lg:inline text-sm">Chat</span>
        </a>
        <a href="#/sessions" class="nav-link flex items-center gap-3 px-4 py-2.5 text-zinc-400 hover:text-zinc-50 hover:bg-zinc-800 transition-colors border-l-2 border-transparent" data-page="sessions">
          <span class="text-base w-5 text-center shrink-0">&#9776;</span>
          <span class="hidden lg:inline text-sm">Sessions</span>
        </a>
        <a href="#/usage" class="nav-link flex items-center gap-3 px-4 py-2.5 text-zinc-400 hover:text-zinc-50 hover:bg-zinc-800 transition-colors border-l-2 border-transparent" data-page="usage">
          <span class="text-base w-5 text-center shrink-0">&#9733;</span>
          <span class="hidden lg:inline text-sm">Usage</span>
        </a>
        <a href="#/settings" class="nav-link flex items-center gap-3 px-4 py-2.5 text-zinc-400 hover:text-zinc-50 hover:bg-zinc-800 transition-colors border-l-2 border-transparent" data-page="settings">
          <span class="text-base w-5 text-center shrink-0">&#9881;</span>
          <span class="hidden lg:inline text-sm">Settings</span>
        </a>
      </div>
      <div class="p-3 border-t border-zinc-800">
        <span id="conn-status" class="flex items-center gap-2 text-xs text-zinc-500">
          <span id="conn-dot" class="w-2 h-2 rounded-full bg-red-500 shrink-0"></span>
          <span id="conn-text" class="hidden lg:inline">Disconnected</span>
        </span>
      </div>
    </nav>

    <!-- Mobile top bar (shown below md) -->
    <div id="mobile-chrome" class="md:hidden fixed top-0 left-0 right-0 z-50 bg-zinc-900 border-b border-zinc-800">
      <div class="flex items-center justify-between px-4 h-12">
        <span class="text-blue-500 font-bold text-base">Flynn</span>
        <span id="conn-status-mobile" class="flex items-center gap-1.5 text-xs text-zinc-500">
          <span id="conn-dot-mobile" class="w-2 h-2 rounded-full bg-red-500 shrink-0"></span>
        </span>
      </div>
      <!-- Page pill indicator -->
      <div id="page-pills" class="flex justify-center gap-1 pb-2 px-4">
        <button class="page-pill px-2.5 py-1 text-xs rounded-full transition-colors bg-zinc-800 text-zinc-400" data-hash="#/">Dashboard</button>
        <button class="page-pill px-2.5 py-1 text-xs rounded-full transition-colors bg-zinc-800 text-zinc-400" data-hash="#/chat">Chat</button>
        <button class="page-pill px-2.5 py-1 text-xs rounded-full transition-colors bg-zinc-800 text-zinc-400" data-hash="#/sessions">Sessions</button>
        <button class="page-pill px-2.5 py-1 text-xs rounded-full transition-colors bg-zinc-800 text-zinc-400" data-hash="#/usage">Usage</button>
        <button class="page-pill px-2.5 py-1 text-xs rounded-full transition-colors bg-zinc-800 text-zinc-400" data-hash="#/settings">Settings</button>
      </div>
    </div>

    <!-- Main content -->
    <main id="content" class="flex-1 overflow-y-auto md:p-6">
      <!-- Mobile: add top padding for fixed bar -->
      <!-- Pages rendered here by router -->
    </main>
  </div>
  <script type="module">
    import { registerPage, initRouter, initStatusIndicator } from './app.js';
    import { DashboardPage } from './pages/dashboard.js';
    import { ChatPage } from './pages/chat.js';
    import { SessionsPage } from './pages/sessions.js';
    import { UsagePage } from './pages/usage.js';
    import { SettingsPage } from './pages/settings.js';

    registerPage('/', DashboardPage);
    registerPage('/chat', ChatPage);
    registerPage('/sessions', SessionsPage);
    registerPage('/usage', UsagePage);
    registerPage('/settings', SettingsPage);

    initStatusIndicator();
    initRouter();
  </script>
</body>
</html>

Step 2: Update manifest theme color

In manifest.webmanifest, change background_color and theme_color to #09090b.

Step 3: Verify build

Run: pnpm build Expected: Clean build (UI files are just copied).

Step 4: Commit

git add src/gateway/ui/index.html src/gateway/ui/manifest.webmanifest
git commit -m "feat(gateway-ui): add Tailwind CDN, Inter font, responsive shell"

Task 2: Replace style.css with Minimal Base

Files:

  • Modify: src/gateway/ui/style.css

Step 1: Replace style.css with minimal overrides

Tailwind handles 90% of styling. Keep only: scrollbar customization, animations (spin, blink), hljs overrides, and a few component styles Tailwind can't cover (scroll-snap, streaming cursor).

Write a new style.css (~120 lines) that covers:

  • Custom scrollbar (webkit)
  • @keyframes spin and @keyframes blink
  • .streaming-cursor::after blinking cursor
  • .tool-event-body toggle (display none/block)
  • .nav-link.active state (since Tailwind can't do [data-active] easily in vanilla)
  • Code block overrides for hljs inside messages
  • Scroll-snap container for mobile swipe (future-proofed but not yet wired)
  • Any remaining custom utility classes not covered by Tailwind

Step 2: Verify build

Run: pnpm build Expected: Clean copy.

Step 3: Commit

git add src/gateway/ui/style.css
git commit -m "refactor(gateway-ui): replace monolithic CSS with Tailwind-compatible base"

Task 3: Rewrite App Router for Responsive Navigation

Files:

  • Modify: src/gateway/ui/app.js

Step 1: Rewrite app.js

The router needs to:

  • Keep hash-based routing
  • Update desktop sidebar active states using Tailwind classes (bg-blue-500/10 text-blue-500 border-l-blue-500)
  • Update mobile pill indicator (active pill gets bg-blue-500 text-white, others bg-zinc-800 text-zinc-400)
  • Add mobile top padding to content area when mobile chrome is visible
  • Wire pill click → location.hash navigation
  • Keep page lifecycle (teardown/render) identical

Connection status indicator needs to update both desktop and mobile elements.

Step 2: Verify build

Run: pnpm build

Step 3: Commit

git add src/gateway/ui/app.js
git commit -m "feat(gateway-ui): responsive router with mobile pill navigation"

Task 4: Rewrite Dashboard Page with Tailwind

Files:

  • Modify: src/gateway/ui/pages/dashboard.js

Step 1: Rewrite all HTML template strings to use Tailwind classes

Key changes:

  • Page title: <h1 class="text-2xl font-semibold text-zinc-50 mb-6">
  • Section titles: <h2 class="text-lg font-semibold text-zinc-50 mb-4 mt-8 pb-2 border-b border-zinc-800">
  • Stat cards: <div class="bg-zinc-900 border border-zinc-800 rounded-lg p-4 hover:border-zinc-600 transition-colors">
  • Stats grid: <div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-4 mb-8">
  • Section collapsible on mobile: wrap sections in <details> element with styled <summary> on small screens, open by default on desktop
  • Tables: Tailwind classes for <table>, <th>, <td> — horizontal scroll wrapper for mobile: <div class="overflow-x-auto">
  • Event stream: <div class="max-h-72 overflow-y-auto bg-zinc-900 border border-zinc-800 rounded-lg p-2 font-mono text-xs">
  • Service cards grid: responsive grid
  • Assistant health chips: compact grid with Tailwind
  • All stat values use font-mono for numbers

Keep all JS logic (fetching, timers, event handlers) identical. Only change the HTML template strings and class names.

Step 2: Verify build

Run: pnpm build

Step 3: Commit

git add src/gateway/ui/pages/dashboard.js
git commit -m "feat(gateway-ui): rewrite dashboard with Tailwind responsive layout"

Task 5: Rewrite Chat Page with Tailwind

Files:

  • Modify: src/gateway/ui/pages/chat.js

Step 1: Rewrite all HTML template strings to use Tailwind classes

Key changes:

  • Chat layout: flex flex-col h-[calc(100vh-48px)] md:h-[calc(100vh-48px)] max-w-3xl
  • Chat header: flex items-center gap-3 pb-3 border-b border-zinc-800 mb-3 flex-wrap
  • Messages container: flex-1 overflow-y-auto flex flex-col gap-3 py-3
  • User messages: self-end bg-blue-500/15 border border-blue-500/25 rounded-lg px-3.5 py-2.5 max-w-[85%]
  • Assistant messages: self-start bg-zinc-900 border border-zinc-800 rounded-lg px-3.5 py-2.5 max-w-[85%]
  • On mobile (<md): messages go full width md:max-w-[85%]
  • Chat input: flex gap-2 pt-3 border-t border-zinc-800
  • Textarea: Inter font, bg-zinc-900 text-zinc-50 border border-zinc-800 rounded-lg px-3 py-2.5 text-sm focus:border-blue-500 outline-none resize-none
  • Send button: bg-blue-500 text-zinc-950 font-semibold px-4 py-2.5 rounded-lg hover:opacity-85 disabled:opacity-40
  • Slash popup: absolute bottom-full left-0 right-0 mb-1 bg-zinc-900 border border-zinc-800 rounded-lg max-h-60 overflow-y-auto z-50 shadow-lg
  • Tool event groups: compact with Tailwind
  • Action buttons (search, attach): pill-shaped with Tailwind
  • Message actions (copy, edit): small icon buttons

Keep all JS logic identical.

Step 2: Verify build

Run: pnpm build

Step 3: Commit

git add src/gateway/ui/pages/chat.js
git commit -m "feat(gateway-ui): rewrite chat page with Tailwind responsive layout"

Task 6: Rewrite Sessions Page with Tailwind

Files:

  • Modify: src/gateway/ui/pages/sessions.js

Step 1: Rewrite all HTML template strings to use Tailwind classes

Key changes:

  • Desktop: table layout with horizontal scroll wrapper
  • Mobile: card-based layout using media query detection in JS (check window.innerWidth < 768)
    • Each session as a card: bg-zinc-900 border border-zinc-800 rounded-lg p-4
    • Session ID, frontend badge, message count, last activity in card
    • View/Delete buttons at bottom of card
  • Filter controls: flex items-center gap-3 mb-4 flex-wrap
  • Select: bg-zinc-900 text-zinc-50 border border-zinc-800 rounded-lg px-3 py-1.5 text-sm
  • Session detail view: card-based message history with scrollable container

Step 2: Verify build

Run: pnpm build

Step 3: Commit

git add src/gateway/ui/pages/sessions.js
git commit -m "feat(gateway-ui): rewrite sessions page with Tailwind card layout"

Task 7: Rewrite Usage Page with Tailwind

Files:

  • Modify: src/gateway/ui/pages/usage.js

Step 1: Rewrite all HTML template strings to use Tailwind classes

Key changes:

  • Summary stats: horizontally scrollable strip on mobile (flex with overflow-x-auto, snap), grid on desktop
  • Per-session table: horizontal scroll wrapper, or stacked cards on mobile
  • Stats grid: grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-4
  • Table: overflow-x-auto wrapper, standard Tailwind table classes
  • Refresh button: secondary button style

Step 2: Verify build

Run: pnpm build

Step 3: Commit

git add src/gateway/ui/pages/usage.js
git commit -m "feat(gateway-ui): rewrite usage page with Tailwind responsive layout"

Task 8: Rewrite Settings Page with Tailwind

Files:

  • Modify: src/gateway/ui/pages/settings.js

Step 1: Rewrite all HTML template strings to use Tailwind classes

Key changes:

  • Each settings section in its own card: bg-zinc-900 border border-zinc-800 rounded-lg p-4 md:p-6 mb-6
  • Toggle switches: styled checkbox with label, min-h-[44px] touch target
  • Text inputs: w-full bg-zinc-950 text-zinc-50 border border-zinc-800 rounded-lg px-3 py-2 text-sm focus:border-blue-500 outline-none
  • Hook pattern textareas: same input styling but multi-line
  • Tool table: horizontal scroll wrapper
  • Services grid: responsive card grid
  • Config JSON view: bg-zinc-950 border border-zinc-800 rounded-lg p-4 max-h-96 overflow-y-auto font-mono text-xs text-zinc-400
  • Button styles: primary (bg-blue-500 text-zinc-950), secondary (bg-zinc-800 text-zinc-50), danger (bg-red-500/15 text-red-500)

Step 2: Verify build

Run: pnpm build

Step 3: Commit

git add src/gateway/ui/pages/settings.js
git commit -m "feat(gateway-ui): rewrite settings page with Tailwind form cards"

Task 9: Update Service Worker Cache Version

Files:

  • Modify: src/gateway/ui/sw.js

Step 1: Bump service worker cache name

Change cache name from flynn-webchat-v2 to flynn-webchat-v3 so browsers pick up the new assets.

Step 2: Commit

git add src/gateway/ui/sw.js
git commit -m "chore(gateway-ui): bump service worker cache version for redesign"

Task 10: Final Build Verification and Cleanup

Step 1: Run full build

Run: pnpm build Expected: Clean build, no errors.

Step 2: Run typecheck

Run: pnpm typecheck Expected: No errors (UI files are plain JS, but ensure no regressions).

Step 3: Run full test suite

Run: pnpm test:run Expected: All tests pass (UI has no unit tests, but ensure nothing else broke).

Step 4: Check the dist output

Run: ls dist/gateway/ui/ and verify all files present.

Step 5: Commit any cleanup

If any adjustments needed, commit them.


Notes for Implementer

  • No TDD for this task — UI files are vanilla JS with no existing test infrastructure. Verification is build + visual.
  • Tailwind CDN — The cdn.tailwindcss.com script is the Play CDN for development. It works without a build step but should not be used for production long-term. This is acceptable for now.
  • Mobile padding — The mobile top bar is fixed, so content needs pt-[calc(3rem+2rem)] (48px bar + 32px pills) top padding on mobile. Handle this in app.js.
  • Preserve all business logic — Only change HTML templates and CSS classes. Do not modify API calls, event handlers, data fetching, or page lifecycle management.
  • escapeHtml() calls — Keep all existing escapeHtml() calls. Security is unchanged.
  • Font loading — Inter is loaded from Google Fonts CDN. JetBrains Mono was already loaded by the browser for users who have it installed; no CDN needed (Tailwind font-mono falls back to system monospace).