diff --git a/docs/superpowers/specs/2026-05-02-phase-2b-form-layout-design.md b/docs/superpowers/specs/2026-05-02-phase-2b-form-layout-design.md new file mode 100644 index 0000000..b9eb552 --- /dev/null +++ b/docs/superpowers/specs/2026-05-02-phase-2b-form-layout-design.md @@ -0,0 +1,134 @@ +# 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 changes` → `unsaved · 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.