Bridge App — Visual Architecture & Design System

Intent

Human: Developer on mobile, managing VPS tmux sessions. Previously using Termius but frustrated with scroll handling and session management.

Must accomplish: Quickly access tmux sessions, view terminal output, send commands, manage windows/panes — all from a touch interface.

Feel: Professional terminal emulator with session management, not a dashboard showing terminals. Dense, functional, command-oriented. Like opening a native terminal app, not a web app.


Domain Exploration

Product Domain

Terminal multiplexing, remote server administration, command-line workflows. The world of SSH, tmux/screen, REPL environments, infrastructure monitoring.

Concepts from this world

Color World

Physical spaces where this product lives:

Actual colors that exist in this domain

Terminal black
#0d1117
ANSI green (desaturated)
#3fb950
ANSI cyan
#58a6ff
ANSI yellow
#d29922
ANSI red
#f85149
Status gray
#30363d

Signature Element

Mobile modifier toolbar: A floating toolbar with Ctrl, Alt, Esc, Tab, arrow keys, and tmux prefix (Ctrl+B). This element is unique to terminal management on mobile — no other product category has this. It should feel like a physical keyboard overlay, not a web UI component.

Defaults to Reject

  1. Generic dashboard layout (sidebar + cards + breadcrumbs) → Replace with: full-screen terminal view with collapsible session drawer
  2. Material Design aesthetic (rounded corners, shadows, floating cards) → Replace with: flat terminal aesthetic, minimal borders, background color shifts for hierarchy
  3. Large padding and breathing room → Replace with: dense layout like real terminals, 4px base unit
  4. Gradient backgrounds → Replace with: flat colors, subtle noise texture optional
  5. Rounded buttons → Replace with: sharp or minimal radius (2px max)

Visual Architecture

Layout Philosophy

Bridge app is a terminal emulator first, not a dashboard. The terminal should dominate the viewport. Session management is secondary — accessible but not always visible.

Desktop (≥1024px)

┌─────────────────────────────────────────┐ │ Sidebar (280px) │ Terminal Area │ │ - Sessions list │ - Active pane │ │ - Windows │ - xterm.js │ │ - Quick actions │ - Full height │ └─────────────────────────────────────────┘

Tablet (768-1023px)

┌─────────────────────────────────────────┐ │ [≡] Session: dev [user ▼] │ ├─────────────────────────────────────────┤ │ │ │ Terminal Area (full width) │ │ │ └─────────────────────────────────────────┘

Sidebar becomes overlay drawer, triggered by hamburger icon.

Mobile (<768px)

┌─────────────────────────────────────────┐ │ [≡] dev:main [•••] │ ├─────────────────────────────────────────┤ │ │ │ │ │ Terminal Area (full screen) │ │ │ │ │ ├─────────────────────────────────────────┤ │ [Esc][Tab][Ctrl][Alt][C-b][↑][↓][←][→] │ └─────────────────────────────────────────┘

Information Hierarchy

Level 1 (Always visible)

Level 2 (Accessible, not always visible)

Level 3 (On-demand)

Spacing System

Base unit: 4px (terminal density)

Spacing scale:
--space-1: 4px   (icon gaps, tight padding)
--space-2: 8px   (button padding, list item spacing)
--space-3: 12px  (card padding, section gaps)
--space-4: 16px  (major section separation)
--space-5: 24px  (page margins on mobile)
--space-6: 32px  (page margins on desktop)

Terminal-specific spacing:

Border Radius Scale

Sharp aesthetic, minimal rounding:

--radius-sm: 2px   (buttons, inputs)
--radius-md: 4px   (cards, modals)
--radius-lg: 8px   (drawer corners, mobile only)
Never use: Large radius (12px+), pill shapes, circular buttons (except avatars).

Design System

Color Tokens

Background hierarchy (darkest to lightest)

--bg-canvas
#0d1117 — Page background
--bg-surface
#161b22 — Cards, sidebar, drawers
--bg-surface-raised
#1c2128 — Dropdowns, modals
--bg-surface-overlay
#2d333b — Tooltips, mobile toolbar

Border hierarchy

--border-default
#30363d — Standard borders
--border-muted
#21262d — Subtle separators

Text hierarchy

--fg-default
#c9d1d9 — Primary text
--fg-muted
#8b949e — Secondary text
--fg-subtle
#6e7681 — Tertiary/disabled

Semantic colors (ANSI-inspired)

--accent-primary
#58a6ff — Cyan/blue, links, active states
--success
#3fb950 — Green, attached sessions, success
--warning
#d29922 — Yellow/amber, warnings
--danger
#f85149 — Red, errors, destructive
Why these colors: Sourced from ANSI terminal palette and GitHub's dark mode (which itself is terminal-inspired). The cyan/blue accent (#58a6ff) is the classic "cyan" from ANSI, desaturated for dark mode. Green (#3fb950) is the classic "green" for success/active states.

Typography

Font families

--font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif;
--font-mono: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'Consolas', 'Liberation Mono', monospace;

Type scale (base 14px, 1.25 ratio for terminal density)

--text-xs: 11px;      /* Captions, badges */
--text-sm: 12px;      /* Secondary text, list items */
--text-base: 14px;    /* Body text, inputs */
--text-lg: 16px;      /* Subheadings */
--text-xl: 18px;      /* Page titles */
--text-2xl: 24px;     /* Hero numbers (session count) */

Font weights

Line heights

Monospace usage

Border & Depth System

Border approach: Minimal borders, rely on background color shifts.

/* Border weights */
--border-width-hairline: 1px;   /* Standard */
--border-width-thick: 2px;      /* Focus rings, active states */

/* Usage */
border: 1px solid var(--border-default);  /* Cards, inputs */
border-bottom: 1px solid var(--border-muted);  /* List separators */
border-left: 2px solid var(--accent-primary);  /* Active session indicator */

Depth strategy: Background color shifts, not shadows.

Elevation 0 (canvas): --bg-canvas
Elevation 1 (surface): --bg-surface
Elevation 2 (raised): --bg-surface-raised
Elevation 3 (overlay): --bg-surface-overlay
No box-shadows except for:

Component States

Interactive elements (buttons, list items, tabs)

/* Default */
background: transparent;
color: var(--fg-default);
border: 1px solid var(--border-default);

/* Hover */
background: var(--hover-bg);

/* Active (pressed) */
background: var(--active-bg);

/* Focus */
outline: 2px solid var(--focus-ring);
outline-offset: 2px;

/* Disabled */
opacity: 0.5;
cursor: not-allowed;

Data states (for SessionList, WindowTabs, etc.)

  1. Loading: Skeleton rows (gray rectangles) or spinner
  2. Empty: Icon + message + CTA button
  3. Error: Red border + error message + retry button
  4. Data: Normal rendering

Component Guide

1. LoginForm

Purpose: Minimal authentication screen.

Layout

Structure

[App logo/name] (24px, mono, centered) [Username input] (full width, 40px height) [Password input] (full width, 40px height) [Sign in button] (full width, 40px height, accent color) [Error message] (if present, danger color, 12px)

Tokens

Implementation notes

2. AppShell

Purpose: Main layout container with responsive sidebar.

Desktop (≥1024px)

display: grid;
grid-template-columns: 280px 1fr;
height: 100vh;
overflow: hidden;

Tablet (768-1023px)

/* Sidebar is overlay drawer */
position: relative;
height: 100vh;
/* Sidebar: position: fixed, left: 0, transform: translateX(-100%) when closed */

Mobile (<768px)

/* Full-screen, sidebar is drawer */
flex-direction: column;
height: 100vh;
height: 100dvh; /* Dynamic viewport for mobile browsers */

3. SessionList

Purpose: Scrollable list of tmux sessions in sidebar.

Structure

[Sidebar header] [App name] [User menu icon] [Session items] (scrollable) [Session 1] (active) [Session 2] [Session 3] [Create session button] (fixed at bottom)

Session item

┌─────────────────────────────┐ │ ● dev 3w │ ← Name (mono) + window count │ attached │ ← Status (muted text) └─────────────────────────────┘

Create session button

States

4. WindowTabs

Purpose: Horizontal tabs for windows within a session.

Desktop layout

┌─────────────────────────────────────────┐ │ [Window 1] [Window 2] [Window 3] │ ├─────────────────────────────────────────┤ │ Terminal area │ └─────────────────────────────────────────┘

Mobile layout (horizontal scroll)

┌─────────────────────────────────────────┐ │ [Win 1] [Win 2] [Win 3] [Win 4] → │ ├─────────────────────────────────────────┤ │ Terminal area │ └─────────────────────────────────────────┘

Tab structure

┌──────────────────┐ │ main [x]│ ← Name + close icon (on hover) └──────────────────┘

Container

States

5. PaneGrid

Purpose: Display multiple panes within a window.

Layout logic

Desktop: 2 panes (vertical split)

┌──────────────────┬──────────────────┐ │ │ │ │ Pane 1 │ Pane 2 │ │ │ │ └──────────────────┴──────────────────┘

Desktop: 2 panes (horizontal split)

┌─────────────────────────────────────┐ │ │ │ Pane 1 │ │ │ ├─────────────────────────────────────┤ │ │ │ Pane 2 │ │ │ └─────────────────────────────────────┘

Mobile

Pane separator

Pane header (if multiple panes)

┌─────────────────────────────────────┐ │ Pane 1: bash [maximize]│ ├─────────────────────────────────────┤ │ Terminal │ └─────────────────────────────────────┘

6. TerminalView

Purpose: xterm.js instance with WebSocket connection.

Structure

┌─────────────────────────────────────┐ │ │ │ xterm.js terminal │ │ (fills container) │ │ │ │ │ └─────────────────────────────────────┘

Container

xterm.js theme

{
  background: '#0d1117',
  foreground: '#c9d1d9',
  cursor: '#58a6ff',
  cursorAccent: '#0d1117',
  selectionBackground: 'rgba(88, 166, 255, 0.3)',
  black: '#0d1117',
  red: '#f85149',
  green: '#3fb950',
  yellow: '#d29922',
  blue: '#58a6ff',
  magenta: '#bc8cff',
  cyan: '#39c5cf',
  white: '#c9d1d9',
  brightBlack: '#484f58',
  brightRed: '#ff7b72',
  brightGreen: '#7ee787',
  brightYellow: '#e3b341',
  brightBlue: '#79c0ff',
  brightMagenta: '#d2a8ff',
  brightCyan: '#56d4dd',
  brightWhite: '#ffffff'
}

Font

Scrollbar

Resize handling

States

7. MobileToolbar

Purpose: Floating keyboard overlay for mobile terminal control.

Layout

┌─────────────────────────────────────────┐ │ [Esc][Tab][Ctrl][Alt][C-b][↑][↓][←][→] │ └─────────────────────────────────────────┘

Structure

Buttons

Button states

Sticky modifier logic

Responsive button layout

Hide/show

8. Button Component

Variants:

Primary (accent)

background: var(--accent-primary);
color: var(--fg-on-emphasis);
border: none;
/* Hover */
background: var(--accent-secondary);

Secondary (outline)

background: transparent;
color: var(--fg-default);
border: 1px solid var(--border-default);
/* Hover */
background: var(--hover-bg);

Danger

background: var(--danger-muted);
color: var(--fg-on-emphasis);
border: none;
/* Hover */
background: var(--danger);

Ghost (minimal)

background: transparent;
color: var(--fg-muted);
border: none;
/* Hover */
background: var(--hover-bg);
color: var(--fg-default);

Sizes

Border-radius: 2px (sharp)

9. Input Component

Structure

[Label] (optional, 12px, muted) [Input field] [Helper text] (optional, 12px, muted or danger)

Input field

States

Placeholder: --fg-subtle

10. Modal Component

Structure

┌─────────────────────────────────────┐ │ │ │ ┌───────────────────────────────┐ │ │ │ Modal title [x] │ │ │ ├───────────────────────────────┤ │ │ │ │ │ │ │ Modal content │ │ │ │ │ │ │ ├───────────────────────────────┤ │ │ │ [Cancel] [Confirm] │ │ │ └───────────────────────────────┘ │ │ │ └─────────────────────────────────────┘

Overlay

Modal container

Header

Content

Footer


Implementation Notes

Tailwind CSS v4 Setup

In web/src/globals.css:

@import "tailwindcss";

@theme {
  --color-canvas: #0d1117;
  --color-surface: #161b22;
  --color-surface-raised: #1c2128;
  --color-surface-overlay: #2d333b;
  
  --color-border-default: #30363d;
  --color-border-muted: #21262d;
  
  --color-text-default: #c9d1d9;
  --color-text-muted: #8b949e;
  --color-text-subtle: #6e7681;
  
  --color-accent: #58a6ff;
  --color-accent-hover: #79c0ff;
  --color-success: #3fb950;
  --color-warning: #d29922;
  --color-danger: #f85149;
  
  --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
  --font-mono: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', Consolas, monospace;
}

Component File Structure

web/src/components/
├── auth/
│   ├── LoginForm.tsx
│   └── ProtectedRoute.tsx
├── layout/
│   ├── AppShell.tsx
│   ├── TopBar.tsx
│   └── Sidebar.tsx
├── session/
│   ├── SessionList.tsx
│   ├── SessionCard.tsx
│   ├── WindowTabs.tsx
│   ├── PaneGrid.tsx
│   └── CreateSessionButton.tsx
├── terminal/
│   ├── TerminalView.tsx
│   ├── TerminalTabs.tsx
│   └── MobileToolbar.tsx
└── ui/
    ├── Button.tsx
    ├── Input.tsx
    ├── Modal.tsx
    └── Spinner.tsx

Key Interactions

  1. Session selection: Click session in sidebar → update route to /sessions/:id → load windows → auto-select first window → show panes
  2. Window switching: Click tab → update route to /sessions/:id?window=:windowId → load panes for that window
  3. Terminal focus: Tap terminal → focus xterm.js → show mobile toolbar (if hidden)
  4. Mobile toolbar: Tap modifier (Ctrl/Alt) → sticky mode → tap key → send key combo → exit sticky
  5. Sidebar drawer (mobile): Swipe right from left edge OR tap hamburger → open drawer → tap outside OR swipe left → close

Animation & Transitions

/* Fast micro-interactions */
--transition-fast: 100ms ease;

/* Standard transitions */
--transition-base: 150ms ease;

/* Drawer/modal animations */
--transition-slow: 200ms ease;

/* Sidebar drawer */
transform: translateX(-100%);
transition: transform var(--transition-slow);
/* Open state */
transform: translateX(0);

/* Modal */
opacity: 0;
transform: scale(0.95);
transition: opacity var(--transition-slow), transform var(--transition-slow);
/* Open state */
opacity: 1;
transform: scale(1);

Accessibility

Performance


Design Decisions Log

Why no shadows?

Terminal emulators are flat. Shadows imply depth/material design, which conflicts with the terminal aesthetic. Background color shifts provide hierarchy without shadows.

Why sharp corners (2px radius)?

Terminals are sharp. Kitty, Alacritty, iTerm2 all use sharp or minimal rounding. Large radius feels "app-like" not "terminal-like".

Why dense spacing (4px base)?

Real terminals are dense. Users want maximum content visibility. Generous padding wastes screen real estate, especially on mobile.

Why ANSI colors?

The product IS a terminal tool. Using ANSI-inspired colors (cyan accent, green success, red danger) reinforces the domain and feels authentic.

Why monospace for session names?

Session names are technical identifiers (like file names or command names). Monospace signals "this is code/technical" and improves readability for mixed-case names.

Why mobile toolbar instead of gestures?

Gestures are discoverable but slow. A toolbar with explicit Ctrl/Alt/Esc buttons is faster and more reliable for terminal workflows. Power users need speed.

Why no breadcrumbs?

The navigation is shallow: session → window → pane. Breadcrumbs add visual noise without value. Top bar shows current context sufficiently.