Two-column CSS Grid for login forms, sticky save bar, and dirty-state header subtitle. Other item types stay single-column with the polish applied. Stacks to single column at <=720px viewport. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
7.4 KiB
Phase 2B: Form Layout (Fullscreen Login)
Date: 2026-05-02
Status: Spec, awaiting review
Surface: Browser extension fullscreen vault UI (extension/src/vault/)
Parent spec: docs/superpowers/specs/2026-04-30-relicario-fullscreen-ux-redesign-design.md (Section A)
Goal
Give the fullscreen login form a desktop-class layout: two-column field arrangement, sticky save bar, and a header that reflects dirty state and save shortcut. Other item types stay single-column for now.
Non-goals
- Two-column layout for
secure_note,identity,card,key,totp,document— single-column stays. - Popup form changes — popup keeps its existing single-column layout.
- New affordances — Phase 2A already shipped the 8 smart inputs.
- Three-pane shell, keyboard nav, command palette — deferred to Phase 3.
Layout
Two-column grid (login only)
┌────────────────────────────────────────────────────────────┐
│ ◀ new login ⌘+S to save │
│ unsaved · esc to cancel │
├──────────────────────────┬─────────────────────────────────┤
│ IDENTITY │ CREDENTIALS │
│ title [required] │ username │
│ url + ⤓ │ password ⊙ ↻ │
│ group (autocomplete) │ strength: ████░ │
│ │ totp secret ◫ │
│ │ live: 492 837 · 23s │
├──────────────────────────┴─────────────────────────────────┤
│ NOTES │
│ ▾ custom sections ▸ attachments │
├────────────────────────────────────────────────────────────┤
│ STICKY SAVE BAR [cancel] [save] │
└────────────────────────────────────────────────────────────┘
CSS rules
- Form pane content:
max-width: 960px,margin: 0 auto. - Two-column wrapper:
display: grid; grid-template-columns: 1fr 1fr; gap: 24px;. - Below 720px viewport:
grid-template-columns: 1fr(single column stack). Use a single@media (max-width: 720px)query. - Notes / custom sections / attachments live in a sibling block below the grid, full-width.
Column assignment (login)
Left column — IDENTITY:
- title (required pill)
- url +
⤓fill-from-tab button + hostname chip below - group input + datalist autocomplete
Right column — CREDENTIALS:
- username
- password +
⊙reveal +↻generate; strength bar renders directly below the input - totp secret +
◫QR button; live preview renders directly below the input
Full-width below grid:
- notes (with
≡mono toggle) - custom sections / fields disclosure
- attachments disclosure
The strength bar and TOTP preview already render inline in Phase 2A — no changes needed beyond ensuring they fit within the column width.
Section headers
Each column gets a small uppercase section header per the parent spec's typography rules:
text-transform: uppercase; letter-spacing: 1px; font-weight: 500;- 1px bottom border in
var(--border-subtle). - Color:
var(--text-muted).
Sticky save bar
- Position:
sticky; bottom: 0;inside the form pane's scroll container. - Background:
var(--bg-pane)with a 24px gradient fade above (linear-gradient(to top, var(--bg-pane) 60%, transparent)). - Content: right-aligned
[cancel] [save]buttons with 12px gap. - The save button reflects validity state — disabled when required fields are empty.
- z-index: above form content, below modals/toasts.
The fade ensures content scrolling behind the bar stays visible without a hard cutoff.
Header treatment
- Title (left):
new loginoredit login, body font, weight 500, size 18px. - Subtitle (left, below title): one of:
unsaved · esc to cancelwhen dirtyno changeswhen pristine- color:
var(--text-muted), size 12px.
- Hint (right):
⌘+S to save(visual only, not a button),var(--text-dim), size 12px. On non-mac, renderCtrl+S. - Popout-to-tab
⤴button: removed from fullscreen header (already done in Phase 1 visual baseline).
The dirty/pristine state is tracked by existing form state. Phase 2B adds a small subscriber that updates the subtitle text on each input change. No new state machine — just a derived boolean.
Other item types
Single-column layout stays for secure_note, identity, card, key, totp, document. These types still get the new sticky save bar and header treatment. Only the column grid is login-specific.
Files touched
| File | Change |
|---|---|
extension/src/vault/vault.css |
Add .form-grid, .form-section-header, .sticky-save-bar, .form-header styles + 720px media query |
extension/src/popup/components/types/login.ts |
Wrap fields into Identity / Credentials column wrappers when rendered into the fullscreen surface |
extension/src/vault/vault.ts |
Wire dirty-state subscriber to header subtitle; render header treatment + sticky bar |
extension/src/vault/__tests__/ |
Tests for two-column rendering, dirty subtitle transitions, sticky bar visibility |
The login renderer needs to know which surface it's rendering into (popup vs fullscreen). The simplest approach: add an optional surface: 'popup' | 'fullscreen' parameter to renderForm(). Default to popup to preserve existing behavior.
Testing
Per-area tests using existing vitest + happy-dom setup:
- Layout test: mount login form with
surface: 'fullscreen', assert grid layout is applied and Identity/Credentials columns contain the expected fields. - Stack-down test: simulate viewport ≤720px, assert single-column layout.
- Dirty subtitle test: mount form, simulate input, assert subtitle changes from
no changes→unsaved · esc to cancel. Assert reversion on reset. - Sticky bar test: assert save bar exists, has correct position style, and save button respects validity.
- Other-type non-regression: mount
secure_note, assert single-column layout (no grid).
No e2e infrastructure changes — manual QA with the rebuilt extension loaded in Chrome.
CLI parity
This phase is purely visual / layout-shaped. No CLI counterpart. The CLI already accepts all login fields as flags (--title, --username, --password, etc.), which is its own form surface.
Out of scope / deferred
- Two-column layout for non-login types (could come in a future phase; current single-column works fine for forms with fewer fields).
- Custom column-width adjustment by the user (drag divider). Fixed 1fr 1fr for now.
- Animated transitions on subtitle text change (just snap).
- Keyboard shortcut to actually save with ⌘+S — that arrives with Phase 3 keymap. The hint is purely a visual label until then.
- Diff view ("you changed 3 fields") — future enhancement.