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.
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.
|
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.
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)
| 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.
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
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.
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.
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.
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.
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.
|
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 | 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 |