Files
relicario/docs/superpowers/specs/2026-05-02-phase-2b-form-layout-design.md
adlee-was-taken 8bf21501a5 docs(spec): Phase 2B form layout (fullscreen login)
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>
2026-05-02 12:55:07 -04:00

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 login or edit login, body font, weight 500, size 18px.
  • Subtitle (left, below title): one of:
    • unsaved · esc to cancel when dirty
    • no changes when 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, render Ctrl+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:

  1. Layout test: mount login form with surface: 'fullscreen', assert grid layout is applied and Identity/Credentials columns contain the expected fields.
  2. Stack-down test: simulate viewport ≤720px, assert single-column layout.
  3. Dirty subtitle test: mount form, simulate input, assert subtitle changes from no changesunsaved · esc to cancel. Assert reversion on reset.
  4. Sticky bar test: assert save bar exists, has correct position style, and save button respects validity.
  5. 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.