The four-frame shell

The shell is the permanent structural frame of a VirtualLife desktop product: four frames, always in the same places, so that position alone tells the user what a region is for. An app bar across the top for identity and account, a destination rail for where you can go, a navigator panel for what you can pick inside the current destination, and a content frame for the material itself. A fifth frame — the status bar — is optional.

✦ VirtualLife
viewport theme
✦
✎3
▦
◈
⟐
⚙
☰ Capture Review Collections / Recent
Page title Subtitle caption
Content frame — glass cards, grids, tables…
16px top-left radius
Optional fifth frame ● Connected ● Sync: —

Shell anatomy

Frame Dimensions Token Notes
App bar Full width × 56px --shell-appbar-h Brand mark and gradient wordmark left; viewport toggle, theme toggle and avatar right. Solid bg-sidebar. No window controls.
Destination rail 60px × full height --shell-rail-w Icon-only, 40px buttons at --radius-md. Active gets --sidebar-accent plus a 3px left accent bar. Settings sits at the bottom.
Navigator panel 252px default, resizable --shell-panel-w Uppercase title, optional filter field, collapsible sections of rows with status dots and right-aligned tabular counts. Collapsible to nothing.
Content frame Remaining space, to the window edges --radius-inset Hairline border, 16px top-left radius only, the wavy canvas behind it. Scrollable, isolated stacking context. Opens with a 48px contextual bar.
Status bar Full width × 28px — optional --shell-statusbar-h Only for products with persistent connection state worth watching. Right-aligned indicators, border-top, --text-xs. Omit it otherwise.
Why this layout? Each frame answers exactly one question — who am I (app bar), where can I go (rail), what is in here (navigator), what am I working on (content) — so nothing has to be labelled twice and the user's material gets the largest frame on screen. The rail keeps every destination one click away without spending width on it; the navigator carries the breadth that would otherwise force a second level of tabs; and the content frame's single rounded corner reads as a window within a window, which is what separates structural chrome from the user's work. As an aside, the top-level arrangement will feel familiar from VS Code, Outlook and WhatsApp Desktop — the difference is the navigator panel, which those apps fold into the sidebar.

Behaviour

Rail overflow. When the destinations outnumber the available height, the tail collapses into a More popover. Measure the real rail height and collapse from it — never switch on a viewport breakpoint, which guesses wrong the moment a product adds a destination.

Rail badges. A count on a destination is a 15px pill in --primary, pinned to the button's top-right. Counts only — never a bare dot for a number the user could read.

Navigator resize and collapse. The panel drags from its trailing edge and remembers its width. The contextual bar's leading toggle collapses it entirely, giving the content frame the full width; collapsed is a legitimate resting state, so nothing important may live only in the panel.

Frame density. --shell-content-zoom scales the content frame's contents and is opt-in: 0.9 for a compact, information-dense product, 1 for a full-size one. The page width caps hold either way.

Page entrance. Content entering the frame animates with omFadeUp — see Motion. The frames themselves never animate; only what they hold.

Section Pattern

Every page is wrapped in a Section component that provides consistent page-level structure: an optional header (title + subtitle) and a scrollable content area.

Gallery Browse and manage your generated images
img
img
img
img
img
img
img
img
img
img

Section Anatomy

Part Prop / Condition Styling
Container Always container pt-4 px-4 h-full overflow-clip
Header When title is set flex items-baseline gap-3 pb-2
Title Required text-lg font-semibold leading-tight
Subtitle Optional text-sm text-muted-foreground leading-tight
Content area Main content h-[calc(100%-2rem)] · vertical scrollbar optional

Content Grids

Image-centric pages use responsive CSS grids that adapt from 1 to 6 columns based on viewport width. Grid gaps are consistent at gap-4 (16px).

Image Grid (Gallery / Generate)

1
2
3
4
5
6
7
8
9
10
11
12
grid-cols-1 → sm:2 → md:3 → lg:4 → xl:5 → 2xl:6
Breakpoint Columns Min Width
Default 1 0px
sm 2 640px
md 3 768px
lg 4 1024px
xl 5 1280px
2xl 6 1536px

Catalog Grid

The catalog uses a slightly different grid: grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4. Cards use aspect-3/4 (portrait) for model preview images.

Model Compare Grid

The compare page uses a CSS Grid with explicit column definitions: grid-template-columns: 240px repeat(N, 256px) 64px. Row height is fixed at 256px. This creates a spreadsheet-like grid with sticky headers and row labels.

Grid rules: 1) Always use gap-4 between grid items. 2) Image tiles use aspect-square by default (1:1). 3) Gallery scroll area has generous horizontal padding (px-12) to prevent images from touching the edges. 4) Grid pagination uses content grouping (by date) with sticky group headers.

Toolbars

Toolbars provide contextual controls above content areas. They use compact components and are visually light — never competing with the content they serve.

Gallery Toolbar

📁 Filter
↕ Size
⟳ Sort
View: Grid

Toolbar Anatomy

Element Height Styling
Container auto px-4 pb-3 space-y-3 shrink-0
Search input h-8 Relative positioned with icon left, max-w-md
Toolbar buttons h-8 Compact ghost/outline buttons, text-xs
Vertical separator h-6 orientation="vertical"
Filter panel auto rounded-lg border bg-card/50 p-3 space-y-3

Selection Bar

When items are selected in the gallery, a selection bar appears: bg-primary/10 px-3 py-2 rounded-lg. It contains a count indicator, compact action buttons (h-7 text-xs), and a "Deselect all" button.

Group Headers (Sticky)

When gallery content is grouped by date, sticky headers use the Glass Heavy recipe: bg-card/80 backdrop-blur-sm sticky top-0 z-10 py-2 px-3 rounded-lg border border-border/50.

Page Templates

Each page follows one of three layout archetypes. Consistency across pages means users build muscle memory quickly.

1. Form Page (Generate, Edit, Settings)

Vertical stack of collapsible glass cards (VLCards), each containing a form group. Scrollable content with a right padding buffer (pr-8).

Structure: Section → VLCard → form fields (flex-col gap-4) → action button at bottom.

Generate Create images from text
Model
Prompt
✦ Generate

2. Grid Page (Gallery, Catalog)

Toolbar on top, scrollable image grid below. The toolbar stays pinned while the grid scrolls. Optional filter panel expands below the toolbar.

Structure: Section → toolbar (shrink-0) → scroll area (flex-1 overflow-y-auto) → responsive grid.

Gallery
Filter
Sort

3. Focus Page (Quick Sort)

Single-item focus with flanking action buttons. The entire viewport is dedicated to one piece of content at a time. Side controls and bottom info strip.

Structure: flex-col h-full → filter bar (optional) → flex-row center (action btns + content + action btns) → info strip.

✕
✓
★
Image (swipeable)
Template selection guide: If the page is about creating or configuring → Form Page. If it's about browsing a collection → Grid Page. If it's about one item at a time → Focus Page.

The phone surface

The phone is the reduced product, never a shrunk desktop. One destination at a time, one level of navigation, and only the material the user came for. No rail, no navigator panel, no breadcrumb.

9:41▮▮▮
‹ Recent ⋯
✦▦◈⚙

Shown at 50% — the frame itself is 390 × 844.

Phone anatomy

Region Dimensions Token Notes
Frame 390 × 844, 40px radius --phone-w / -h / -radius The reference viewport. A 16px inset separates the frame's content from its edge.
Status bar Full width × 38px --phone-status-h Platform-owned. Never draw UI into it.
Header Full width × 52px --phone-header-h Back button, title, at most one action. Buttons are 44px — the touch-target floor, never smaller.
Body Remaining space, scrolling — One column. Glass rows and cards at --radius-lg; the desktop's grids collapse to a list.
Tab bar Full width × 56px --phone-tabbar-h 3–5 destinations, 22–24px icons, active in --primary. This is the phone's entire navigation.
What gets cut. If a destination cannot be expressed as one list and one detail view, it does not belong on the phone. Settings, configuration and anything table-shaped stay on the desktop; the phone carries capture, review and the one decision the user makes away from their desk.

Layering Model

The interface is built in visual layers that create depth without heavy shadows. Glass surfaces at varying opacities are the primary mechanism for layering.

Layer 5: Overlays z-50+ · Dialogs, fullscreen, toasts
Layer 4: Sticky elements z-10–50 · Group headers, selection bars
Layer 3: Interactive surfaces z-auto · Cards, image tiles, toolbars
Layer 2: Content cards z-auto · VLCards, settings sections
Layer 1: Background z-0 · Wavy SVG, bg-background
Layer 0: Chrome Fixed · App bar, rail, navigator panel
Layer Glass Level z-index Elements
0 – Chrome Solid (opaque) Fixed/100 App bar, rail, navigator panel
1 – Background — 0 Wavy SVG, bg-background, gradient canvas
2 – Content Light (10%) auto VLCards, form sections, settings panels
3 – Interactive Medium (40%) auto Image tiles, filter panels, edit overlays
4 – Sticky Heavy (80%) 10–50 Group headers, selection bars, popovers
5 – Overlay Scrim (black/80) 50+ Dialogs, fullscreen viewer, toasts
Key insight: Glass opacity increases with z-elevation. Background content shows through less as layers stack upward. This creates natural visual hierarchy — the most interactive surfaces demand the most attention by being more opaque, while content cards recede into the ambient background.