UI Visual Reference -- L2¶
Reference Source¶
This document translates the standalone handoff into implementation rules. The reference is .claude-design/Posthaste.standalone.bundled.html, with readable source under .claude-design/handoff/posthaste/project/src/.
The handoff default is theme = dark, preset = neutral, density = standard, layout = 3. All dimensions below target that state unless noted.
Global Tokens¶
Use the token values from L0-branding. No component should introduce unregistered type sizes, icon sizes, radii, or signal colors.
Default typography:
- Body and UI text: Geist.
- Metadata, counters, dates, keyboard hints, code-like values: Geist Mono.
- Display font: Fraunces only for rare display moments, not for the normal app shell.
Default icons:
- Toolbar chip icons:
14px. - Sidebar row icons:
14px. - Sidebar disclosure chevrons:
12px. - Table header flag/attachment icons:
12px. - Reader action icons:
14px. - Modal close icons:
16px.
All lucide-style icons use the stroke value attached to their token size.
Root Shell¶
The root shell fills the viewport. Embedded mode has no border radius, no outer card shadow, and no decorative background. The root background is bg; text defaults to fg.
The shell is a vertical flex layout:
ActionBarat the top.- Main horizontal pane group below.
- Overlays render absolutely above the shell.
The main pane group is horizontal:
- Sidebar:
210pxstandard,180pxcompact. - Splitter:
1pxvisible rule,8pxhit area. - Message list:
420pxstandard,360pxcompact,520pxin two-pane list-only mode. - Splitter: same as above.
- Reader: flexes, minimum
280px.
Pane splitters:
- Background:
border. - Cursor:
col-resize. - Hover/active overlay:
3pxwidth,accent.coral, centered over the1pxrule. - Hit area:
4pxon each side of the visible rule.
Action Bar¶
The action bar height is:
- Compact:
38px - Standard:
42px - Roomy:
46px
Structure, left to right:
- Traffic lights.
8pxspacer.- Compose chip.
- Separator.
- Reply, reply-all, forward chips.
- Separator.
- Archive, trash, flag, snooze, tag chips.
- Flexible spacer.
- Query search.
- Shortcut button.
- Settings button.
- Theme button.
The action bar background is bgTitlebar. It has a 1px bottom border in borderSoft, horizontal padding 12px, and 4px gap between adjacent toolbar items.
Traffic lights:
- Three circles, each
12px. - Gap:
8px. - Colors:
#ff5f57,#febc2e,#28c940. - Each circle has
inset 0 0 0 0.5px rgba(0,0,0,0.2).
Toolbar chips:
- Height:
28px. - Border: none.
- Radius:
6px. - Font: Geist
12px, weight500. - Icon size:
14px. - Gap between icon and label:
5px. - Icon-only padding:
0 6px. - Label chip padding:
0 9px 0 8px. - Inactive foreground:
fgMuted. - Hover background:
hoverBg. - Active background:
accent.coralSoft. - Active foreground:
accent.coralDeep. - Transition: background over about
0.1s.
Keyboard hints inside labeled toolbar chips:
- Font: Geist Mono
11px. - Opacity:
0.6. - Padding:
1px 4px. - Radius:
3px. - Background:
rgba(128,128,128,0.12). - Left margin:
2px.
Separators:
- Width:
1px. - Height:
18px. - Background:
borderSoft. - Margin:
0 6px.
Icon-only utility buttons for shortcuts, settings, and theme:
- Size:
28pxsquare. - Radius:
5px. - Background: transparent.
- Foreground:
fgMuted. - Shortcut button uses Geist Mono
13px, weight700. - Settings and theme icons use
14px.
Command Search¶
Resting command-search control:
- Icon button,
28pxsquare. - Radius:
6px. - Background:
bgElev. - Border:
1px solid borderSoft. - Icon: command/search glyph,
14px,fgFaint. - Cursor: default button pointer.
Active filter chip:
- Height:
28px. - Max width:
24rem. - Radius:
6px. - Border:
1px solid focusRing. - Box shadow:
0 0 0 2px color-mix(in oklab, focusRing 18%, transparent). - Font: Geist Mono
11px. - Contains the applied query and an icon-only clear button.
Sidebar¶
The sidebar fills its pane and scrolls vertically with class ph-scroll.
Base:
- Background:
bgSidebar. - Right border:
1px solid border. - Width:
100%. - Bottom padding:
12px.
Section order:
- Quick filters:
All Inboxes,Flagged. Smart.Tags.Accounts.
Top quick filter block:
- Padding:
10px 0 2px. All InboxesusesIcons.All, count, andaccent.coral.FlaggedusesIcons.Flag, count, andsignal.flag.
Section headers:
- Padding:
14px 14px 6px. - Font: Geist Mono
11px, weight600. - Color:
fgFaint. - Letter spacing:
0.06em. - Text transform: uppercase.
- Display: flex, align center, justify between.
Accountsheader addsmargin-top: 8px.
Smart section header add button:
- Size:
16pxsquare. - Icon: plus,
12px. - Background: transparent.
- Default color:
fgFaint. - Hover color:
accent.coral. - Hover background:
hoverBg. - Radius:
3px.
Sidebar rows:
- Role: treeitem.
- Focusable.
- Height:
24pxcompact,28pxstandard,32pxroomy. - Margin:
0 6px. - Padding:
0 8px 0 calc(8px + depth * 14px). - Gap:
8px. - Radius:
5px. - Font: Geist
13px. - Weight:
500, or600when selected. - Default background: transparent.
- Hover background:
hoverBg. - Selected background:
selBg. - Default foreground:
fg. - Selected foreground:
selFg. - Focus ring:
0 0 0 2px focusRing. - User select: none.
Sidebar row left structure:
- A
14pxspacer precedes the icon. This aligns rows with account children and disclosure affordances. - Icon size is
14px. - Unselected icon color is row accent if supplied, otherwise
fgMuted. - Selected icon color is
selFg. - Label truncates with ellipsis.
Sidebar row counters:
- Display only when count is greater than
0. - Font: Geist Mono
11px, weight600. - Unselected color:
fgFaint. - Selected color:
selFg. - Quick row counters are plain text, not filled pills.
Smart rows:
| Mailbox | Icon | Accent |
|---|---|---|
| Relevant | Sparkle |
accent.coral |
| Read Later | Snooze |
accent.amber |
| Bills | Tag |
accent.violet |
| Newsletters | Layers |
accent.sage |
| Today | Bolt |
accent.blue |
When a smart row has an edit handler and is hovered, the count is replaced by an edit rules button:
- Size:
18px. - Icon: sliders,
11px. - Radius:
3px. - Color:
fgFaint, orselFgif selected. - Background: transparent.
Tags:
- Use tag icon at
14px. - Icon color is the tag color.
- Tag rows do not show counts unless backend provides them.
Account headers:
- Height:
30pxstandard,26pxcompact. - Margin:
6px 6px 2px. - Padding:
0 10px. - Gap:
8px. - Radius:
5px. - Cursor: pointer.
- Disclosure button:
14px, transparent,fgMuted, opacity0.7, rotated90degwhen expanded. - Stamp:
18pxsquare, radius4px, account color background, white text, Geist Mono11px, weight700. - Account label: Geist
12px, weight700, colorfg. - Unread pill: background
signal.unread, color#fff, Geist Mono11px, weight700, padding1px 6px, radius6px, min width18px, centered.
Account mailbox children:
- Use
depth = 1. - Use mailbox role icons.
- Use normal row counter treatment, not account-header filled pills.
Message List¶
The message list is a tabular conversation list in standard and compact densities. It is not a card list.
Pane:
- Background:
bgList. - Pane borders are supplied by the resizable splitters, not by an internal message-list border.
- Overflow hidden at the pane boundary.
- Role:
listbox. - Header and rows live in one table-width layout inside a horizontal
ph-scrollwrapper. - When enabled columns exceed the pane width, the entire message list table scrolls horizontally as one unit.
- When enabled columns fit within the pane, the table stretches to the full pane width so the left edge and right edge align with the pane edges.
- The header bottom separator uses the shared app separator color
border / 80%, matching the main pane splitters and toolbar separator. - Horizontal overscroll uses the base list surface
bgList; zebra striping is only painted by actual row elements so it ends with the real message list content. bgListAltis intentionally brighter thanbgList; it is a subtle zebra contrast inside the list palette, not the neutral app background.
Known unresolved issue:
- Native horizontal elastic overscroll exposes the scroll container surface, so it does not currently continue the row zebra pattern. A sticky row-surface zebra plane was rejected because it misaligned with virtual rows and did not reliably paint native overscroll. Until a cleaner browser-owned or component-owned model is found, row-owned zebra striping with neutral overscroll is the accepted behavior.
Columns have two layout types:
- Fixed columns render as a fixed pixel basis and do not absorb surplus pane width.
- Stretch columns render as
minmax(basis, grow)and absorb surplus pane width after fixed columns keep their basis. - Resize always changes the stored logical basis, never the rendered width after stretch has been applied.
Default columns:
| ID | Type | Basis | Resizable | Label | Alignment |
|---|---|---|---|---|---|
unread |
fixed | 28px |
No | circle icon | center |
flag |
fixed | 28px |
No | flag icon | center |
attach |
fixed | 28px |
No | attachment icon | center |
threadSize |
fixed | 72px |
Yes | Count |
right |
subject |
stretch, grow 1 |
320px |
Yes | Subject |
left |
from |
fixed | 180px |
Yes | From |
left |
date |
fixed | 128px |
Yes | Date Received |
left |
account |
fixed | 72px |
Yes | Account |
right |
tags |
stretch, grow 0.5 |
140px |
Yes | Tags |
left |
Minimum widths:
subject:120pxthreadSize:56pxfrom:80pxdate:80pxaccount:54pxtags:60px
Stretch columns absorb extra pane width when the pane is wider than the raw column total. In the default set, subject is the primary stretch column and tags is the secondary stretch column. Right-side columns remain aligned to the right edge because the table itself stretches to the pane.
Header:
- Height:
26px. - Display: grid with the same column template as rows.
- Background:
bgTitlebar. - Bottom border:
1px solid borderStrong. - Width equals the table width.
Header cells:
- Width: column width.
- Padding:
0 10px. - Resizable cells reserve right padding for the resize divider.
- Font: Geist Mono
11px, weight600. - Text transform: uppercase.
- Letter spacing:
0.06em. - Default color:
fgFaint. - Sorted color:
fg. - Sortable hover color:
fgMuted. - Sortable hover background:
hoverBg. - Display: flex, align center, gap
4px. - Justification follows alignment.
- Header content is clipped inside the cell and truncates with ellipsis; labels, icons, and sort indicators must not paint beyond their column bounds.
- Sorted indicator is
↑or↓with opacity0.8. - Flag and attachment headers render icons at
12px.
Column dividers:
- Width:
1px. - Height:
100%of header. - Background: shared app separator color
border / 80%. - Cursor:
col-resize. - Hit area:
8pxtotal, centered on the visible divider. - Hover/active highlight: same-height
1pxcoral line. Dividers do not grow or stretch on hover. - Header resize handles have three placements:
interior,start-edge, andend-edge. interiorhandles are centered between columns and use an8pxhit area.- The first resizable column also gets a
start-edgehandle when it is at the table edge; it expands inward, uses a16pxhit area, and reverses drag delta so dragging left grows the column. - The last visible column uses an
end-edgehandle when resizable; it expands inward, uses a16pxhit area, and its normal divider is transparent so it remains selectable without creating extra horizontal scroll width or drawing into the pane splitter.
Rows:
- Height:
24pxcompact,30pxstandard,48pxroomy. - Width equals the table width.
- Display: grid with the same column template as the header.
- Background: selected
selBg; hoverhoverBg; zebra oddbgListAlt; otherwisebgList. - Text color: selected
selFg; unreadfg; readfgMuted. - Font: Geist
13pxstandard,12pxcompact. - Weight:
600unread,400read. - Focus state: inset
0 0 0 2px focusRing. - Cursor: pointer.
Row cells:
- Width equals header column width.
- Fixed columns
unread,flag,attachhave no horizontal padding. - Other cells use
0 10pxplus right padding for divider breathing room. - Display: flex, align center.
- Gap:
6px. - Overflow hidden; all text and badges truncate or clip inside the cell. No row content may visually overlap an adjacent column.
- Sender, subject, account/source, and tag text use the same row body typography. Account/source labels are not smaller mono metadata.
Unread dot:
- Size:
7px. - Radius: circular.
- Unselected background:
signal.unread. - Selected background:
selFg.
Flag icon:
- Size:
12px. - Unselected color:
signal.flag. - Selected color:
selFg.
Attachment icon:
- Size:
12px. - Unselected color:
fgFaint. - Selected color:
selFg.
Thread count column:
- Font: Geist Mono
11px, weight600. - Text color: selected
selFg, otherwisefgMuted. - Text is right-aligned and tabular.
- Shows the conversation message count as plain table text, including
1. - Appears immediately before
subjectin the default column order.
Subject column:
- Subject text truncates.
- Subject uses row font and row weight.
- The standard tabular handoff does not show preview text in the same row.
From column:
- Shows sender display name.
- Truncates.
- Uses row font and row color.
Date column:
- Font: Geist Mono
11px. - Color: selected
selFg, otherwisefgSubtle. - Shows full date label from the data, for example
Yesterday, 18:04.
Account column:
- Font: Geist Mono
11px. - Color: selected
selFg, otherwisefgFaint. - Text maps accounts to compact labels such as
Gmail,Work,Univ..
Tags column:
- Up to three tags.
- Gap:
4px. - Tag pill font: Geist Mono
11px, weight600. - Text color: selected
selFg, otherwise tag color. - Background: selected
rgba(255,255,255,0.12), otherwisecolor-mix(in oklab, tagColor 14%, transparent). - Padding:
0 5px. - Radius:
3px. - Line height:
14px. - Label truncates to the first four characters in tabular mode.
Roomy density:
- Uses a two-line card-like row instead of tabular columns.
- Padding:
10px 12px. - Gap:
10px. - Border bottom:
1px solid borderSoft. - Contains unread dot rail, sender/date line, subject line, preview line, and full tag chips.
Reader Pane¶
The reader pane is the right-hand article surface.
Empty state:
- Full width and height.
- Background:
bgReader. - Centered flex column.
- Gap:
14px. - Color:
fgFaint. - Stamp:
72px, colorfgFaint, textNO MSG, dateSELECTED. - Text:
Select a message to read, Geist13px, weight500. - Navigation hint: Geist Mono
11px, includesJandKkbd pills. - Kbd pills: padding
1px 5px, radius3px, border1px solid borderSoft, backgroundbgElev.
Loaded state:
- Role:
article. - Background:
bgReader. - Color:
fg. - Overflow: auto.
- Uses
ph-scroll.
Reader header:
- Padding:
16px 20px 12px. - Bottom border:
1px solid borderSoft. - Position: relative.
- Header inner row: flex, align start, gap
12px.
Subject:
- Font: Geist
17px. - Weight:
600. - Color:
fg. - Letter spacing:
-0.2px. - Margin bottom:
8px. - Line height:
1.25.
Sender block:
- Avatar size:
28pxcircular. - Avatar background:
color-mix(in oklab, accent.coral 40%, bg). - Avatar text: white, Geist
12px, weight700. - Sender row gap:
10px, wraps when needed. - Sender name: Geist
13px, weight600, colorfg. - Sender email: Geist Mono
11px, colorfgMuted, wrapped in angle brackets. - Recipient/date line: Geist Mono
11px, colorfgMuted, margin top2px. - Recipient/date format:
to <recipient> · <date>.
Reader action buttons:
- Size:
28pxsquare. - Radius:
5px. - Background: transparent.
- Hover background:
hoverBg. - Color:
fgMuted. - Icon size:
14px. - Focus ring:
0 0 0 2px focusRing. - Actions: reply, forward, archive, more.
Reader tag strip:
- Display only when the message has tags.
- Margin top:
10px. - Gap:
6px. - Tag font: Geist Mono
11px, weight600. - Color: tag color.
- Background:
color-mix(in oklab, tagColor 14%, transparent). - Padding:
2px 7px. - Radius:
4px. - Each tag contains a
5pxcircular dot in tag color plus label.
Progress bars:
- Use the shared
ProgressBarprimitive for component loads and process meters. - Track height:
4pxcompact,6pxstandard. - Track radius: full pill.
- Track background:
borderSoft. - Indicator radius: full pill.
- Default indicator color:
brand-coral; domain-specific process meters may use a semantic accent such as sync blue. - Determinate progress transitions width over roughly
300ms. - Indeterminate progress uses a single moving segment and must respect
prefers-reduced-motion. - Labels, when visible, use
fgFaint/muted text at11pxcompact or12pxstandard.
Attachment strip:
- Display only when attachments exist.
- Padding:
10px 20px. - Bottom border:
1px solid borderSoft. - Background:
bgElev.
Attachment rows:
- Display: flex, align center.
- Gap:
10px. - Padding:
8px 10px. - Background:
bg. - Border:
1px solid border. - Radius:
6px. - Margin bottom:
6pxexcept last row. - Previewable attachment rows are clickable across the full row, with hover and focus states matching quiet reader action treatment.
Attachment type tile:
- Size:
32pxsquare. - Radius:
4px. - Text: uppercase Geist Mono
11px, weight700, white. pdf:accent.rose.image:accent.violet, labelimg.ai: labelai.- Other files:
accent.blue.
Attachment text:
- Filename: Geist
12px, weight500, colorfg, single line ellipsis. - Size: Geist Mono
11px, colorfgFaint. - Actions: preview, download, and more icon buttons, using the reader action button treatment. Preview uses the eye icon. Download and more remain secondary actions; row click itself opens the preview surface.
Reader body:
- Padding:
18px 22px 28px. - Font: Geist
13px. - Line height:
1.6. - Color:
fg. - White space: pre-wrap for plain text.
- Width: fills the available reader pane.
- Body does not center itself in the handoff; it begins at the left padding. HTML iframe content must inherit the reader background rather than painting a default white slab. If an email's own HTML sets a background, that email-owned styling may show.
- Links use
accent.coralDeep, no underline, with1px dotted accent.coralbottom border.
Floating Panel¶
Command search, keyboard shortcuts, and compose use the shared floating panel.
Overlay:
- Absolute inset
0. - Z-index:
2500. - Align top center.
- Padding top:
54px. - Pointer-events none on the overlay root and pointer-events auto on the panel.
- No background scrim or backdrop blur; the app remains visible and interactive.
- Outside pointer interaction closes the panel unless it is pinned.
- Dragged panel offset and user size are persisted locally and restored on the next open.
- Floating panels are user-resizable within the viewport via edge and corner handles on the panel sheet (all four edges plus all four corners; corners drive both axes). Initial sizes come from a shared 12-column by 8-row floating size grid; a resized panel may shrink to the preset minimum (or the shared
min-w-72/min-h-48floor) and grow up to the usable viewport. The grabbed edge follows the pointer while the opposite edge stays fixed. - During a move drag, show faint guide rails sized to the panel: left/center/right modal-width columns and top/bottom modal-height rows, with no fill. When the panel reaches a rail it resists movement for
12pxbefore breaking out, and the active rail highlights while resisting. - During a resize drag, show a static grid — the fixed 12-column by 8-row layout grid anchored to the viewport (column lines across the usable width, row lines down the usable height below the
54pxtop offset), independent of the panel being dragged so every window standardizes to the same dimensions. Draw only the axis being resized (vertical lines for width, horizontal for height, both at a corner); idle grid lines are fainter than the movement rails, and the snapped line highlights. The dragged edge resists each grid line for12pxbefore breaking out. A small monospacecols × rowschip centered on the panel reports its size in whole grid cells. (Movement rails stay dynamic — sized to the current panel — so the two systems read differently: move shows where the window will sit, resize shows standard sizes.) - Double-clicking a resize handle grows the panel out to the nearest grid line(s): an edge expands that side, a corner expands both grabbed sides. Holding
Option/Altalso expands the opposite edge of each affected axis — so an edge grows on both sides and a corner grows on all four. The grow animates (≈150ms); live drags never animate.
Panel sheet:
- Default size: caller-specific grid preset, max width constrained to the viewport minus the shared
16pxside margins. - Max width:
calc(100vw - 32px). - Background: soft diagonal gradient mixing
brand-coral,ring, andpanel; the mix must derive from root theme variables so light, dark, and glass presets keep contrast. - Border:
1px solid color-mix(in oklab, brand-coral 22%, border). - Radius:
14px. - Shadow:
0 28px 80px rgba(0,0,0,0.24)in light mode, stronger in dark mode. - Overflow: hidden.
- Text color: theme foreground tokens, not hard-coded white.
- Font: Geist.
- Move, pin, and expand/restore icon buttons sit at the left of the input row. The grip button and any empty space in the header row both drag the panel (the drag is suppressed when it starts on an interactive control), persisting the final placement; pin keeps it open during outside interaction; expand temporarily fills the viewport, hides the resize handles, and restores back to the floating panel.
- The close button is icon-only and aligned to the far right independently of header content width.
Command Palette¶
The command palette opens from Cmd/Ctrl+K and the command-search control.
Palette sheet:
- Width: shared command preset:
6 / 12viewport-grid columns, minimum360px, maximum640px. - Uses the shared floating panel shell.
Input row:
- Display: flex, align center.
- Padding:
0 16px. - Bottom border:
1px solid borderSoft. - Search icon:
18px,fgMuted. - Input height:
48px. - Input padding:
0 12px. - Font size:
16px. - Placeholder:
Search messages, contacts, commands.... - Right close button: icon-only
X, aligned to the far right.
Results:
- Max height:
440px. - Padding:
6px 0. - Group label: padding
4px 16px, Geist Mono11px, uppercase, letter spacing0.7px, weight600, colorfgFaint. - Row: full width, display flex, align center, gap
10px, padding8px 16px, border none. - Active row background: theme hover background.
- Row font: Geist
13px. - Icon:
15px,fgMuted. - Subtext: Geist
12px,fgMuted, max width240px. - No visible keyboard shortcut hints are shown in the command palette.
Settings Sheet¶
Settings opens as a centered sheet, not a route replacement.
Backdrop:
- Absolute inset
0. - Z-index:
2100. - Background: dark
rgba(6,4,12,0.55), lightrgba(40,30,60,0.35). - Backdrop filter:
blur(18px) saturate(140%). - Display: flex, center.
- Padding:
32px. - Animation: modal fade in around
0.18s.
Sheet:
- Width:
100%. - Max width:
1080px. - Height:
100%. - Max height:
760px. - Background: dark
rgba(22,20,28,0.88), lightrgba(253,252,250,0.94). - Border:
1px solid border. - Radius:
16px. - Dark shadow:
0 32px 80px rgba(0,0,0,0.55), 0 0 0 1px rgba(255,255,255,0.04) inset. - Display: flex.
- Overflow: hidden.
- Font: Geist.
Rail:
- Width:
220px. - Flex shrink:
0. - Background:
rgba(0,0,0,0.12). - Right border:
1px solid borderSoft. - Padding:
16px 0. - Header padding:
0 16px 14px. - Header uses
PostmarkStampat20pxinaccent.coral. - Header title: Geist
17px, weight700, letter spacing-0.3px. - Footer: pinned bottom, padding
12px 16px, Geist Mono11px, colorfgFaint, textv1.0.0 · JMAP 0.3.
Rail items:
- Sections: Accounts, Mailboxes & Rules, Appearance, Signatures, Privacy & Tracking, Keyboard, Automation, About.
- Display: flex, align center, gap
10px. - Padding:
8px 14px. - Margin:
1px 8px. - Radius:
7px. - Border: none.
- Font: Geist
13px. - Weight:
600active,500inactive. - Inactive background: transparent.
- Inactive text:
fg. - Inactive icon:
fgMuted. - Active background:
accent.coralSoft. - Active text/icon:
accent.coralDeep. - Icon size:
15px.
Content:
- Flex column.
- Header row: display flex, align center, padding
14px 22px, bottom border1px solid borderSoft. - Title: Geist
17px, weight700, letter spacing-0.3px. - Close button:
30pxsquare, radius8px, transparent,fgMuted, icon16px. - Scroll body: flex
1, overflow auto, padding22px.
Settings account rows:
- Section label: Geist Mono
11px, uppercase, weight600, colorfgFaint, letter spacing0.7px. - Account list gap:
10px, margin bottom20px. - Row display: flex, align center, gap
12px. - Row padding:
14px. - Background:
bgElev. - Radius:
10px. - Border:
1px solid borderSoft. - Stamp:
38pxsquare, radius8px, account color background, white text, Geist Mono13px, weight700. - Account label: Geist
13px, weight600. - Meta: Geist Mono
11px,fgMuted, text likeJMAP · sync ok · 12 mailboxes. - Actions: ghost modal buttons
Edit,Remove. - Add account: primary modal button with plus icon.
Smart mailbox rows:
- Header row has section label left and primary
New smart mailboxbutton right. - Row display: flex, align center, gap
12px. - Padding:
12px. - Background:
bgElev. - Radius:
10px. - Border:
1px solid borderSoft. - Cursor: pointer.
- Icon tile:
30pxsquare, radius8px, backgroundcolor-mix(in srgb, accent 20%, transparent), foreground matching the smart mailbox accent, icon14px. - Name: Geist
13px, weight600. - Summary: Geist Mono
11px,fgMuted, text like5 unread · 3 match conditions · 2 actions. - Chevron:
14px,fgFaint.
Appearance rows and other settings form rows:
- Display: flex row unless stacked.
- Gap:
16px. - Padding:
12px 0. - Bottom border:
1px solid borderSoft. - Label column width:
180px. - Label: Geist
13px, weight500, colorfg. - Hint: Geist
11px, colorfgMuted, margin top3px, line height1.4. - Control area flexes.
Modal buttons:
- Height:
32px. - Padding:
0 14px. - Radius:
8px. - Gap:
6px. - Font: Geist
12px, weight600. - Primary background:
accent.coral, text#fff. - Primary border:
color-mix(in srgb, black 12%, transparent). - Primary shadow:
0 1px 0 rgba(255,255,255,0.2) inset, 0 2px 6px rgba(0,0,0,0.12). - Secondary background:
bgElev, textfg, borderborder. - Ghost background: transparent, text
fg. - Danger background: transparent, text and border
accent.rose.
Inputs and selects:
- Height:
32px. - Padding: inputs
0 10px; selects0 28px 0 10px. - Border:
1px solid border. - Radius:
8px. - Background:
bg. - Text:
fg. - Font: Geist
13px, or Geist Mono when marked mono. - Focus border:
focusRing. - Focus shadow:
0 0 0 3px color-mix(in srgb, focusRing 20%, transparent).
Switches:
- Track:
34pxby20px, radius999px. - Checked background:
accent.coral. - Unchecked background:
border. - Thumb:
16px, white, top2px, left16pxchecked or2pxunchecked. - Thumb shadow:
0 1px 3px rgba(0,0,0,0.2).
Mailbox Editor¶
Mailbox editor is a focused settings page inside the Mailboxes & Rules category.
Page:
- Uses the settings page max width and scroll behavior.
- Back link returns to the mailbox index.
- Source and smart mailbox editors share the same header and section rhythm.
Header:
- Display: flex, align center, gap
12px. - Includes mailbox icon, mailbox name, type/source metadata, and optional reorder controls.
- Smart mailbox definition uses the recursive rule builder.
- Source mailbox definition uses the server role selector.
- Actions use the shared automation rule list/editor, separated from the definition section.
- Type label: Geist Mono
11px,fgMuted,SMART MAILBOXorMAILBOX RULES. - Close button:
30pxsquare, radius8px, icon16px.
Tabs:
- Present for smart mailboxes.
- Padding:
0 22px. - Bottom border:
1px solid borderSoft. - Tab padding:
10px 14px. - Active text:
fg, weight600. - Inactive text:
fgMuted, weight500. - Active underline:
2px solid accent.coral.
Match mode segmented control:
- Aligns right in the tab row.
- Background:
bg. - Border:
1px solid borderSoft. - Radius:
7px. - Padding:
3px. - Segment padding:
3px 10px. - Font: Geist Mono
11px, uppercase, letter spacing0.5px, weight600. - Active background:
bgElev.
Editor content:
- Padding:
20px 22px. - Scrolls with
ph-scroll. - Condition/action rows use
bgElev,borderSoft, radius10px. - Footer padding:
14px 22px, top border1px solid borderSoft. - Footer hint: Geist Mono
11px,fgMuted.
Compose Panel¶
Compose uses the shared floating panel shell with a mail-window header.
Sheet:
- Width: shared compose preset:
8 / 12viewport-grid columns, minimum560px, maximum780px. - Max width:
calc(100vw - 32px). - Height: shared compose preset:
6 / 8viewport-grid rows, minimum460px, maximum640px, capped to the viewport below the54pxtop offset and16pxbottom margin. - Background, radius, border, shadow, move/pin/close controls, outside-close behavior, and persisted placement come from the shared floating panel.
- Flex column.
Header:
- Height:
40px. - Padding:
0 14px. - Gap:
10px. - Background:
bgTitlebar. - Bottom border:
1px solid borderSoft. - Top radius:
12px. - Traffic lights:
12px, colors match action bar. - Center title:
New Message, Geist12px, weight600, colorfgMuted, letter spacing0.2px.
Fields:
- Field rows use compact label-plus-control layout.
- From stamp is
14pxsquare, radius3px, account color, white Geist Mono9px, weight700. - Recipient inputs use Geist
13px, transparent background. - Subject input uses Geist
13.5px, weight500.
Body:
- Editor minimum height:
220px. - Padding:
14px 16px 8px. - Font: Geist
13.5px. - Line height:
1.55. - Background:
bgReader. - The editor is a single inline-rendered Markdown source surface with no split/preview toolbar.
- Markdown formatting controls are exposed through keyboard shortcuts and the editor context menu.
- Markdown delimiters remain editable source characters; recognized spans are styled in place.
Footer:
- Padding:
10px 12px. - Gap:
6px. - Top border:
1px solid borderSoft. - Background:
bgElev. - Bottom radius:
12px. - Send button is coral, with
Cmd+Enterkbd hint. - Tool buttons include attach, AI assist, template, follow-up, tracking, encrypt.
Backend gaps may disable schedule, tracking, encryption, templates, and AI controls, but the visual placement should remain reserved.
Shared Modal Primitive¶
Backdrop:
- Absolute inset
0. - Z-index:
2000. - Display center.
- Dark background:
rgba(6,4,12,0.55). - Light background:
rgba(40,30,60,0.35). - Backdrop filter:
blur(18px) saturate(140%).
Sheet:
- Max width:
calc(100% - 48px). - Max height:
calc(100% - 48px). - Dark background:
rgba(22,20,28,0.82). - Light background:
rgba(255,255,254,0.85). - Backdrop filter:
blur(24px) saturate(180%). - Border: dark
1px solid rgba(255,255,255,0.09), light1px solid rgba(20,18,28,0.08). - Radius:
16px. - Dark shadow:
0 32px 80px rgba(0,0,0,0.55), 0 0 0 1px rgba(255,255,255,0.04) inset. - Flex column, overflow hidden.
Modal header:
- Padding:
18px 22px 16px. - Gap:
12px. - Bottom border:
1px solid borderSoft. - Optional icon tile:
36pxsquare, radius10px, backgroundaccent.coralSoft, coloraccent.coralDeep. - Title: Geist
17px, weight700, letter spacing-0.3px. - Subtitle: Geist
12px,fgMuted, margin top2px. - Close:
30pxsquare, radius8px, icon16px.
Modal footer:
- Padding:
14px 22px. - Gap:
8px. - Top border:
1px solid borderSoft. - Background:
color-mix(in srgb, currentColor 2%, transparent).
Status Bar¶
The handoff includes a status bar component even though the current shell may omit it.
Status bar:
- Height:
24px. - Padding:
0 12px. - Gap:
16px. - Top border:
1px solid borderSoft. - Background:
bgSidebar. - Font: Geist Mono
11px. - Color:
fgMuted. - Left text:
<count> messages · <unread> unread. - Sync indicator:
6pxsage dot plusJMAP · Fastmail · sync ok. - Right account label.
Scrollbars¶
Elements with ph-scroll use thin custom scrollbars:
- Scrollbar width/height:
8px. - Track: transparent.
- Thumb:
rgba(128,128,128,0.25). - Thumb radius:
4px. - Hover thumb:
rgba(128,128,128,0.4).
Assertions¶
| ID | Sev. | Assertion |
|---|---|---|
| default-dark-neutral | MUST | The default shell uses dark neutral tokens, not the old light-first theme or the glass preset |
| locked-type-ramp | MUST | UI components use only the documented type ramp unless this spec is updated |
| signal-separation | MUST | Coral, blue, and slate-blue remain separate brand/flag, unread, and selection signals |
| actionbar-order | MUST | The action bar uses the reference control order from traffic lights through theme toggle |
| sidebar-order | MUST | The sidebar section order is quick filters, Smart, Tags, Accounts |
| account-counter-blue | MUST | Account-header unread counters use signal.unread as a filled blue pill with white text |
| message-list-tabular | MUST | Standard density message rows are tabular rows, not card rows |
| row-height-standard | MUST | Standard density message rows are 30px high |
| header-row-alignment | MUST | Message list header cells and row cells share the same effective column widths |
| reader-body-width | MUST | Reader body content uses a 720px maximum readable width |
| settings-centered-sheet | MUST | Settings opens as a centered modal sheet over the live app shell |
| overlays-glass-only | SHOULD | Glass blur is reserved for overlays and modals, not the default app background |