Files
ChatApp/design_handoff_chatapp
byGalax 0ca29952ba
Release desktop app / build (, windows-latest) (push) Has been cancelled
Release desktop app / build (--target universal-apple-darwin --bundles app,updater, macos-14) (push) Has been cancelled
feat: profile avatar upload + share_conv_keys rpc + favicon + smtp tweaks
2026-04-19 23:04:03 +02:00
..

Handoff: ChatApp — Clean Rail Redesign + Discord-Style Call UI

Overview

This package contains the full design spec for redesigning the existing ChatApp (Netralax) interface around a Clean Rail layout aesthetic, plus a complete Discord-style call system (incoming call, in-call dock, picture-in-picture, fullscreen cinema mode).

The redesign covers:

  1. The main chat interface (icon-rail sidebar + chat list + conversation area) in both light and dark mode.
  2. The call UX: incoming call overlay → active call docked above the chat (so users can keep typing while on a call) → fullscreen cinema mode → minimizable PiP bubble.

About the Design Files

The files in this bundle are design references created in HTML/React/CSS — prototypes showing the intended look, layout, and behavior. They are not production code to copy verbatim.

Your task is to recreate these designs inside the existing ChatApp codebase, using its established patterns (existing React components, styling conventions, state management, icon library, etc.). If some tokens or helper components don't exist yet in the codebase, add them but keep them aligned with what's already there.

The design files are organized as:

  • ChatApp Redesign.html — entry HTML that boots the prototype
  • components/app.jsx — main layout (rail + chat list + chat area, plus call state machine)
  • components/call.jsx — all call UI components (IncomingCallScreen, ActiveCallScreen, ParticipantTile, CallControls, PipCall)
  • components/icons.jsx — SVG icon set used
  • components/data.jsx — dummy chat/message data
  • styles/base.css — shared base tokens + layout primitives
  • styles/variants.cssvariant-clean light + dark token definitions (this is the look you're targeting)
  • styles/rail.css — rail-layout specifics
  • styles/call.cssall call UI styles (the most important file for the call feature)
  • screenshots/ — reference images of each state

Fidelity

High-fidelity. Exact colors, radii, spacing, typography and motion are specified. Recreate pixel-perfectly using the codebase's existing component library — but prefer the codebase's primitives over duplicating the prototype's raw CSS where equivalents exist.


Design Tokens (Clean Rail)

Colors — Light mode (.variant-clean)

--bg:        #fafaf9   (app background)
--bg-2:      #f4f4f2   (secondary surfaces, hover, search input)
--bg-3:      #ffffff   (elevated surfaces — chat list, chat area, cards)
--fg:        #0a0a0f   (primary text)
--fg-muted:  #737380   (secondary text, icons)
--line:      rgba(10,10,15,0.08)  (dividers, borders)
--accent:    #4F46E5   (indigo — primary actions, active states, my-bubble)
--accent-fg: #ffffff

Colors — Dark mode (.variant-clean.dark)

--bg:        #050507
--bg-2:      #111118
--bg-3:      #0A0A0F
--fg:        #f4f4f5
--fg-muted:  #8a8a99
--line:      rgba(255,255,255,0.07)
--accent:    #6D73FF   (slightly lighter indigo for contrast)
--accent-fg: #ffffff

Status / Semantic Colors (both modes)

Speaking/Online/Success:  #16a34a light · #22c55e / #4ade80 dark
Danger/Decline/Hangup:    #dc2626 (hover #b91c1c) · #fb7185 dark accent
Warning/Idle:             #f59e0b
Live/Recording:           #ef4444
E2EE indicator (light):   #16a34a  (dark: #4ade80)

Avatar color swatches (assigned per chat)

violet:  bg #ddd6fe / fg #5b21b6   (dark: #312E81 / #C4B5FD)
amber:   bg #fde68a / fg #78350f
rose:    bg #fecdd3 / fg #881337
teal:    bg #99f6e4 / fg #134e4a   (dark: #134e4a / #99f6e4)

Typography

  • Family: 'Inter', system-ui, sans-serif — Inter is the base. Display/headline use 'Outfit' (participant name in IncomingCall).
  • Scale:
    • Chat title / participant name large: 24px / 700
    • Primary labels, chat name: 1415px / 600
    • Body (bubbles, messages): 14px / 400500
    • Meta, timestamps, presence: 1112px / 400500
    • Rail tooltips, badges: 1011px / 500600
    • Uppercase eyebrow (e.g. "EINGEHENDER ANRUF"): 12px / 600 / letter-spacing 0.12em

Spacing

  • Container padding: 16px (call-stage), 1420px (topbars, composer)
  • Tile gap in grid: 810px
  • Control-button row gap: 810px
  • Border radius scale: 4 (small), 8 (tiles/badges), 10 (buttons), 14 (cards, large buttons), 18 (bubbles, bottom-fs controls), 24 (modal/card)

Shadows

  • Elevated card light: 0 20px 60px rgba(0,0,0,0.1)
  • Elevated card dark: 0 20px 60px rgba(0,0,0,0.5)
  • Accent button: 0 4px 14px rgba(22,163,74,0.3) (green accept) / same pattern for accent
  • PiP glow: 0 12px 40px rgba(0,0,0,0.2), 0 0 0 3px rgba(79,70,229,0.15)

Motion

  • Standard UI transition: all 0.15s
  • Pulse ring (incoming call): 2s ease-out infinite, scale 0.85→1.25, opacity 0.6→0
  • Audio pulse (speaking ring): 1.3s ease-out infinite, scale 1→1.3
  • Live dot: 1.5s ease-in-out infinite, opacity 1↔0.4
  • PiP entrance: slideInCall 0.4s cubic-bezier(0.22,1,0.36,1)
  • Fullscreen hint fade: 3.5s total (015% fade in, 75100% fade out)

Screens / Views

1. Main Chat — Clean Rail Layout

Structure (left → right):

  • Icon Rail (72px wide): brand logo at top, tab buttons (Chats / Friends / Admin), spacer, Settings + theme-toggle at bottom. Active tab shows a vertical pill indicator on the left edge.
  • Chat List (280px wide): header with title + "new chat" icon, search input, list of ChatItems (avatar + name + time + preview, optional unread dot), rail-user footer showing own profile + quick mute/settings.
  • Chat Area (flex, fills rest): chat header (counterpart avatar + name + presence/handle + search/voice/video buttons), messages scroll area, composer at bottom.

Chat header actions (right side): search icon, phone icon, video icon. Phone triggers voice call (callScreen → 'active', callState.video = false), Video triggers video call (callState.video = true).

Message bubbles:

  • Mine (outgoing): filled accent bg, white text, radius 18px 18px 4px 18px.
  • Theirs (incoming): transparent bg, 1px line border, fg text, radius 18px 18px 18px 4px.
  • Avatars appear only on the last message of a run from that sender.
  • Reactions: pill row below bubble, .mine = filled accent.
  • System events ("Kaiwandi hat einen Anruf gestartet — Anruf beitreten"): centered event-pill, muted bg, muted fg, 11px.

Composer: plus button (attach), rounded input bar with inline emoji + mic icons, send button.


2. Incoming Call Overlay

Full-bleed overlay replacing the chat area (takes over .chat-area space), radial-gradient background tinted with accent.

Centered card (420px wide):

  • Eyebrow "EINGEHENDER ANRUF" (uppercase, 12px, tracked)
  • Large avatar (96px) with two pulsing rings (staggered 1s)
  • Caller name (24px / 700 / Outfit)
  • Subtitle row: lock icon + "E2E verschlüsselt · Voice Call"
  • Two side-by-side action buttons:
    • Ablehnen: transparent, red border/text, hover = 8% red bg
    • Annehmen: green #16a34a, white, shadow

3. Active Call — Docked (Default)

CRITICAL layout decision: The active call does NOT take over the chat area. Instead it renders as a dock above the messages, taking roughly 50% of the chat-area height (min 280px, max 420px). The chat header is hidden while the dock is active (redundant — call topbar shows the channel name). Messages + composer remain fully visible and usable below, so users can keep chatting during the call. This is the Discord desktop pattern.

Dock content:

  • Call topbar: left side shows group icon + "Die Squad" + duration (tabular-nums), with E2E verschlüsselt below in success-green. Right side has 3 mode toggles: Grid / Focus / Fullscreen.
  • Call stage: fills the remaining space, renders either:
    • Grid: CSS grid, 2×2 for 4 people, 3×2 for 56, 1fr 1fr for 2.
    • Focus: main speaker tile takes most height, bottom strip with 110px-tall smaller tiles for others.
    • Fullscreen: switches to mode === 'fullscreen' — see screen 4.
  • Call controls bar at the bottom of the dock: Mic, Video, Screen-share, Participants, Hangup (wider, red). Compact 36px buttons when docked.

4. Active Call — Fullscreen Cinema Mode

When mode = fullscreen:

  • Entire call-dock container becomes absolute-positioned and takes over everything below the window bar (chat list still visible on the left).
  • The call topbar is hidden entirely.
  • Main speaker fills the whole frame, no border/radius.
  • Participants strip moves to a floating column in the bottom-right (.fs-strip): 180px wide, 100px tall tiles, glass-blurred bg.
  • Call controls bar floats at bottom center — glass pill: rgba(10,10,15,0.7) + backdrop-filter: blur(20px) + subtle border + 18px radius.
  • "Esc zum Verlassen" hint fades in top-center on entry and auto-fades out over 3.5s. Pressing Esc or clicking Grid/Focus buttons exits.
  • No visible X button (intentional — keyboard-only exit, confirmed design decision).

5. Participant Tile — Shared Component

A participant is either in audio-only, video, or sharing screen mode.

  • Tile border: 1px var(--line), radius 14 (10 in dock). Hover → border becomes accent. .speaking → 2px green border + green glow shadow. .focused → accent border.
  • Audio-only: centered avatar (72px large, 44px small). When speaking, a green ring pulses around the avatar (::before pseudo).
  • Video: placeholder stub (gradient background + centered avatar, since real video not in scope here).
  • Screenshare: renders a "fake window" — chrome row with 3 dots, content row with grey skeleton lines + one accent-tinted block. A glass badge top-left: monitor icon + "Max teilt Bildschirm" (badge hidden on small tiles).
  • Meta row (absolute, bottom 8px, glass): left shows crown (if me) + name + lock (if E2EE), right shows muted mic icon (red chip) and/or sharing icon (green chip).

6. Picture-in-Picture (PiP)

A compact 260px pill docked bottom-right of the chat area:

  • 44px preview square (avatar of speaker, or mini shared-window placeholder if someone's sharing)
  • Title "Die Squad · {participantCount}"
  • Subtitle row: red pulsing dot + "Live · tippe zum Öffnen"
  • Circular red hangup button on the right
  • Whole thing is clickable → expands back to active call
  • Entrance animation: slide from bottom + fade in (slideInCall)
  • Shadow: standard + 3px accent glow ring

Interactions & Behavior

Call State Machine (lives in ChatApp)

// Primary state
const [callScreen, setCallScreen] = useState('none'); // 'none' | 'incoming' | 'active' | 'pip'
const [callMode, setCallMode]     = useState('grid'); // 'grid' | 'focus' | 'fullscreen'
const [focusedId, setFocusedId]   = useState(firstSharingOrSpeakerId);
const [callState, setCallState]   = useState({ muted: false, sharing: false, video: false, duration });

Triggers & Transitions

  • Chat-header phone icon click → setCallScreen('active'), callState.video = false
  • Chat-header video icon click → setCallScreen('active'), callState.video = true
  • Grid tile click → setFocusedId(id); setCallMode('focus')
  • Focus/Grid/Fullscreen buttons in call topbar → set mode directly
  • Incoming "Annehmen" → setCallScreen('active')
  • Incoming "Ablehnen" → setCallScreen('none')
  • Hangup → setCallScreen('none')
  • PiP clicked → setCallScreen('active')
  • PiP hangup (circle red btn) → stopPropagation() + setCallScreen('none')
  • Esc key (global listener while callScreen === 'active' && callMode === 'fullscreen') → setCallMode('grid')

Chat Header Visibility Rule

if (callScreen === 'active' && callMode !== 'fullscreen') → hide chat header
else → show chat header

Keyboard

  • Esc exits fullscreen (only). No other global keybindings.

State Management

Data shapes to wire up (see components/call.jsx):

type Participant = {
  id: string;
  name: string;
  initial: string;
  color: 'violet' | 'amber' | 'rose' | 'teal';
  speaking: boolean;
  muted: boolean;
  video: boolean;
  sharing?: boolean;
  me?: boolean;
  e2ee: boolean;
};

type CallState = {
  muted: boolean;
  sharing: boolean;
  video: boolean;
  duration: string; // "HH:MM:SS"
};

In production these all come from your real-time signaling layer (WebRTC, LiveKit, etc.). The prototype uses static data for layout purposes only — wire them to your call store.


Assets

No custom imagery. Everything is SVG icons (inlined in components/icons.jsx) and CSS. Fonts from Google Fonts: Inter, Outfit.

Icons used in this scope: Chat, Friends, Shield, Settings, Logo, AddUser, Search, Phone, Video, Plus, Smile, Mic, MicOff, Send, Sun, Moon, Monitor, Lock, Crown, Grid, Focus, Maximize, PhoneOff.

Replace with your codebase's icon library if one exists (e.g. lucide-react, heroicons) — names match lucide conventions.


Implementation Checklist

  1. Tokens: add the Clean-variant light + dark CSS variables to your theme layer. If you already have a token system, map them to your existing names; otherwise create --bg, --bg-2, --bg-3, --fg, --fg-muted, --line, --accent, --accent-fg.
  2. Icon rail + chat list + chat area: refactor the current top-nav layout into the 3-column rail layout. Use your existing Avatar, ChatItem, Message components — only restyle.
  3. Message bubbles: outlined them / filled me, asymmetric radius, avatars only on last-of-run.
  4. Call state machine: add the four useState hooks to whatever component owns the chat screen. Hook the Esc key effect.
  5. IncomingCallScreen: new component, full-bleed overlay of .chat-area.
  6. Call dock: new component, renders above .messages. Hide chat-header while docked.
  7. ParticipantTile: audio / video / sharing modes, speaking ring, meta overlay.
  8. Fullscreen mode: switch call container to position absolute, floating strip + floating controls + Esc hint.
  9. PiP: minimizable widget bottom-right of chat area.
  10. Wire real data: replace CALL_PARTICIPANTS dummy with your live call-session participants.

Files in This Handoff

design_handoff_chatapp/
├── README.md                    (this file)
├── ChatApp Redesign.html        (entry — open in browser to see the prototype)
├── components/
│   ├── app.jsx                  (chat app + rail layout + call state machine)
│   ├── call.jsx                 (Incoming / Active / Pip / ParticipantTile / CallControls)
│   ├── icons.jsx                (all SVG icons)
│   └── data.jsx                 (dummy chats, messages)
├── styles/
│   ├── base.css
│   ├── variants.css             (Clean light + dark tokens — this is your theme)
│   ├── rail.css                 (rail layout)
│   └── call.css                 (all call UI — highest priority file)
└── screenshots/
    ├── 01-chat-light.png
    ├── 03-chat-dark.png
    ├── 04-incoming-dark.png
    ├── 05-incall-grid-dark.png
    ├── 06-incall-focus-dark.png
    ├── 07-incall-fullscreen-dark.png
    ├── 09-pip-dark.png
    └── 11-incall-light.png

Notes for Claude Code

  • The Clean Rail variant is Variante 2 in the prototype's variant switcher — ignore the other 5 aesthetic variants (Playful, Y2K, Cyberpunk, Warm, Brutal), they were design exploration and not the chosen direction.
  • Prefer composition over copying raw CSS. If the codebase has a Card, Button, Tooltip, use them.
  • Duration ("00:12:47") should tick live from the call start timestamp, not be static.
  • For WebRTC/media handling: this package specifies UI only. The media/signaling layer is out of scope and already exists in the codebase (see CallUI.tsx, InCallPanel.tsx).