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 index b9eb552..b3c8506 100644 --- 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 @@ -1,31 +1,241 @@ -# Phase 2B: Form Layout (Fullscreen Login) +# Phase 2B: Polish Foundation + Form Layout **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) +**Surface:** Browser extension — popup, fullscreen vault, setup wizard +**Parent spec:** `docs/superpowers/specs/2026-04-30-relicario-fullscreen-ux-redesign-design.md` ## 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. +Bring the extension up to a "professional, contained" feel — like opening 1Password or another polished password manager — without losing the terminal-monospace soul. Three surfaces (login popup, setup wizard, fullscreen vault) all get the same polish vocabulary applied. Phase 2B also lands the two-column login form layout from the parent spec. + +## What changed from the original Phase 2B scope + +The original Phase 2B was scoped to form layout only. After visual review, we expanded scope to include: + +- A **patina** palette shift — gold accent dialed from bright `#d2ab43` toward weathered `#a88a4a`/`#cdb47a`/`#5a3f12`. Red theca dialed from saturated `#9a1a1a` toward brick `#7d2622`. +- **Logo update** — same composition, patina palette, translucent gradient gem. +- **Polish vocabulary** — backdrop with subtle radial glow + 18px grid texture, glass cards (translucent panels with backdrop-blur), refined typography lockup, primary/secondary button hierarchy. +- **Arrow glyph** — `▸` (U+25B8) for "next" buttons, matching the `▾`/`▸` disclosure glyphs already in use. + +These items now ship together so the form layout lands inside an already-polished surface, rather than as a layout change inside flat CSS. ## 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. +- New affordances — Phase 2A already shipped the 8 smart inputs. +- Light theme — single dark theme stays. +- Mobile/narrow layouts under 720px — popup handles narrow. +- Animated transitions / motion — focus state is the only transition. +- Item types other than `login` getting a two-column treatment. -## Layout +## Visual language -### Two-column grid (login only) +The polish vocabulary lives in `extension/src/popup/styles.css` and `extension/src/vault/vault.css`. Both files share token definitions and class names where possible. + +### Palette (patina) + +```css +:root { + /* Patina gold — replaces the bright amber */ + --gold-base: #a88a4a; /* base, less yellow / more bronze */ + --gold-mid: #cdb47a; /* duller mid-highlight */ + --gold-shadow: #5a3f12; /* deeper bronze shadow */ + --gold-text: #c9a868; /* legible on dark, brand text */ + --gold-soft: rgba(184,149,86,0.14); /* hover/active fill */ + --gold-ring: rgba(184,149,86,0.18); /* focus ring */ + --gold-stroke: #b89556; /* default border on emphasized elements */ + + /* Surface — slightly deeper than current bg */ + --bg-base: #0a0e14; /* page (was #0d1117) */ + --bg-pane: #11161e; /* slightly elevated surface */ + --bg-card: rgba(22, 27, 34, 0.55); /* glass card fill */ + --bg-input: #0a0e14; /* matches base for sunken feel */ + + /* Borders */ + --border-soft: rgba(255,255,255,0.05); /* card edges */ + --border-mid: #262d36; /* input borders */ + --border-warm: #2a3140; /* slightly warmer for vault.css */ + + /* Text */ + --text: #c9d1d9; + --text-muted: #8b949e; + --text-dim: #6b7888; +} +``` + +The bright accent token `--accent: #d2ab43` is renamed to `--gold-base: #a88a4a`. Aliases keep existing component code working during the migration (`--accent: var(--gold-base)`). + +### Backdrop + +A reusable backdrop applied to popup body, setup wizard body, and the vault shell: + +```css +.surface-backdrop { + position: relative; + background: + radial-gradient(ellipse 700px 240px at 50% -40px, rgba(184,149,86,0.05), transparent 65%), + linear-gradient(180deg, #11161e 0%, #0a0e14 100%); +} +.surface-backdrop::before { + content: ''; + position: absolute; + inset: 0; + background-image: + linear-gradient(rgba(255,255,255,0.012) 1px, transparent 1px), + linear-gradient(90deg, rgba(255,255,255,0.012) 1px, transparent 1px); + background-size: 18px 18px; + pointer-events: none; +} +.surface-backdrop > * { position: relative; z-index: 1; } +``` + +The radial top-glow opacity is intentionally low (`0.05`) so it doesn't wash out on cheaper monitors. The grid texture is barely visible (`0.012` white) — adds a sense of "place" without becoming busy. + +### Glass card + +Used for the unlock card, setup step card, mode-picker cards, and form section cards (Identity / Credentials): + +```css +.glass { + background: rgba(22, 27, 34, 0.55); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + border: 1px solid rgba(255,255,255,0.05); + border-radius: 10px; + box-shadow: + 0 1px 0 rgba(255,255,255,0.03) inset, + 0 6px 18px rgba(0,0,0,0.35); +} +``` + +Browsers without `backdrop-filter` support fall back gracefully — the card stays semi-translucent over the backdrop without the blur. + +### Buttons + +Two clear tiers: + +```css +.btn-primary { + background: var(--gold-base); + color: var(--bg-base); + border: none; + padding: 9px 14px; + font-size: 12px; + font-weight: 600; + border-radius: 6px; + letter-spacing: 0.3px; +} +.btn-primary:hover { background: #c9a868; } + +.btn-secondary { + background: transparent; + border: 1px solid rgba(255,255,255,0.06); + color: var(--text-muted); + padding: 6px 12px; + font-size: 11px; + border-radius: 5px; +} +``` + +Existing `.btn` class keeps existing styling for backwards compatibility; new `.btn-primary` / `.btn-secondary` are used in updated views. + +### Typography lockup + +Logo + brand + tagline group with tighter spacing on login and setup: + +- Logo mark: 40-44px square, 9-10px corner radius, inner highlight only (no outer glow). +- Brand text: weight 600, color `var(--gold-text)`, letter-spacing 0.5px. +- Tagline: 11px, `var(--text-dim)`, letter-spacing 0.3px. + +### Inputs + +```css +.input { + background: var(--bg-input); + border: 1px solid var(--border-mid); + color: var(--text); + padding: 9px 10px; + border-radius: 6px; + transition: border-color 0.15s, box-shadow 0.15s; +} +.input:focus { + outline: none; + border-color: var(--gold-stroke); + box-shadow: 0 0 0 2px var(--gold-ring); +} +``` + +### Arrow glyph + +The `▸` (U+25B8, small right triangle) replaces ASCII `→` in "next" buttons. Reuses the existing disclosure-glyph vocabulary already used in `▾ custom sections` / `▸ attachments`. + +## Logo update + +`extension/icons/relicario-logo.svg` and `extension/icons/relicario-logo-16.svg` updated: + +- Gold gradient stops shifted: `#d2ab43 → #f5d97a → #7c5719` becomes `#a88a4a → #cdb47a → #5a3f12`. +- Red theca radial: `#9a1a1a → #3a0a0a` becomes `#7d2622 → #2c0d0a`. +- Highlight gradient: `#fde9a8 → #d2ab43` becomes `#dac8a0 → #a88a4a`. +- Solid gold tones (`#7c5719`, `#fff3cf`, `#8a5e1c`) remapped to patina equivalents. +- Center asterisk gem now translucent: facets use vertical gradients (`gemFacetLight` / `gemFacetDark`) that fade to transparent at the tip; gem core uses a radial glass gradient (`gemCore`); two refraction highlights replace the single white-yellow dot. + +The composition (pedestal, theca, gem, hinge collar, fleur-de-lis) is unchanged. + +## Surface-by-surface changes + +### Login popup (`extension/src/popup/`) + +`unlock.ts` view: + +``` +┌──────────────────────────────────┐ +│ [logo] │ +│ Relicario │ +│ two-factor vault │ +│ │ +│ ┌─[ glass card ]──────────────┐ │ +│ │ UNLOCK │ │ +│ │ [passphrase input ] │ │ +│ │ [ unlock vault ] │ │ ← btn-primary, full width +│ └──────────────────────────────┘ │ +│ │ +│ [open vault] [settings] │ ← btn-secondary, demoted +└──────────────────────────────────┘ +``` + +- Body gets `.surface-backdrop`. +- Logo lockup grouped (logo / brand / tagline) with tighter spacing (8-12px between). +- Form moves into `.glass` card with `UNLOCK` label inside. +- Primary action is a real button ("unlock vault") — replaces the "press Enter to submit" implicit flow. +- Open-vault and settings demoted to secondary buttons below the card. + +### Setup wizard (`extension/src/setup/`) + +- Body gets `.surface-backdrop`. +- Header lockup at top (logo + "Relicario vault setup"). +- Progress dots get a tiny shadow on the current step (`box-shadow: 0 0 4px rgba(184,149,86,0.4)`). +- Each `wizard-step` becomes a `.glass` card. +- Mode-picker cards become smaller `.glass` cards with patina active state. +- All "next" buttons use `▸` glyph. + +### Fullscreen vault (`extension/src/vault/`) + +- Body gets `.surface-backdrop`. +- Form section panels (Identity, Credentials) are `.glass` cards. +- Save bar matches glass treatment with translucent fill + backdrop-blur. +- Form layout switches to two-column for login (see below). + +## Form layout (login, fullscreen only) + +The original Phase 2B scope: ``` ┌────────────────────────────────────────────────────────────┐ -│ ◀ new login ⌘+S to save │ +│ edit login ⌘+S to save │ │ unsaved · esc to cancel │ ├──────────────────────────┬─────────────────────────────────┤ -│ IDENTITY │ CREDENTIALS │ +│ [ glass: IDENTITY ] │ [ glass: CREDENTIALS ] │ │ title [required] │ username │ │ url + ⤓ │ password ⊙ ↻ │ │ group (autocomplete) │ strength: ████░ │ @@ -39,11 +249,11 @@ Give the fullscreen login form a desktop-class layout: two-column field arrangem └────────────────────────────────────────────────────────────┘ ``` -### CSS rules +### Layout 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. +- Below 720px viewport: `grid-template-columns: 1fr` (single column stack). - Notes / custom sections / attachments live in a sibling block below the grid, full-width. ### Column assignment (login) @@ -55,80 +265,74 @@ Give the fullscreen login form a desktop-class layout: two-column field arrangem **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 +- password + `⊙` reveal + `↻` generate; strength bar below the input +- totp secret + `◫` QR button; live preview 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. +### Sticky save bar -### Section headers +- `position: sticky; bottom: 0;` inside the form pane scroll container. +- Translucent fill matching the glass vocabulary, with a 24px gradient fade above (content scrolls under). +- Right-aligned `[cancel] [save]` buttons. +- Save button reflects validity state — disabled when required fields are empty. -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)`. +### Header treatment -## Sticky save bar +- Title (left): `new login` / `edit login`, weight 500, size 18px. +- Subtitle (left, below title): `unsaved · esc to cancel` (dirty) or `no changes` (pristine), `var(--text-muted)`, size 12px. +- Hint (right): `⌘+S to save` (visual only — actual save shortcut arrives in Phase 3 keymap), `var(--text-dim)`, size 12px. Renders `Ctrl+S` on non-mac. +- Popout-to-tab `⤴` removed from fullscreen forms (already done in Phase 1). -- 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. +### Other item types -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. +Single-column stays for `secure_note`, `identity`, `card`, `key`, `totp`, `document`. They still get the new glass-card treatment around the form section, 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 | +| `extension/icons/relicario-logo.svg` | Patina palette + gradient glass gem | +| `extension/icons/relicario-logo-16.svg` | Patina palette (toolbar size) | +| `extension/src/popup/styles.css` | Patina tokens, `.surface-backdrop`, `.glass`, `.btn-primary/secondary` | +| `extension/src/popup/components/unlock.ts` | Logo lockup, glass card, primary unlock button | +| `extension/src/popup/components/types/login.ts` | Add `surface: 'popup' \| 'fullscreen'` param; column wrapping when fullscreen | +| `extension/src/setup/setup.ts` | `.surface-backdrop`, glass step cards, glass mode-picker cards, `▸` arrows | +| `extension/src/setup/setup.html` | Body wrapper class | +| `extension/src/vault/vault.css` | Patina tokens, glass form sections, form-grid, sticky save bar, header treatment | +| `extension/src/vault/vault.ts` | Header subtitle dirty-state subscriber, surface flag passed to login renderer | +| `extension/src/vault/components/*.ts` | Glass class on form panels | -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. +The login renderer needs to know which surface it's rendering into (popup vs fullscreen). 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). +1. **Palette migration test:** computed style on `.btn-primary` resolves to `#a88a4a`. +2. **Layout test:** mount login form with `surface: 'fullscreen'`, assert grid layout, Identity / Credentials columns contain expected fields. +3. **Stack-down test:** simulate viewport ≤720px, assert single-column. +4. **Dirty subtitle test:** mount form, simulate input, assert subtitle text changes. +5. **Sticky bar test:** assert save bar exists, position style, save button validity. +6. **Glass class application:** unlock card, setup step card, form panels all get `.glass` class. +7. **Arrow glyph test:** all "next" buttons render `▸` (no `→`). +8. **Other-type non-regression:** mount `secure_note`, assert single-column layout. +9. **Logo regression:** snapshot test on `relicario-logo.svg` defs/colors. -No e2e infrastructure changes — manual QA with the rebuilt extension loaded in Chrome. +Manual QA pass per surface with the rebuilt extension loaded in Chrome and Firefox. ## 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. +This phase is purely visual / layout-shaped. No CLI counterpart. The CLI already accepts all login fields as flags (`--title`, `--username`, `--password`, etc.). ## 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. +- Two-column layout for non-login types. +- User-resizable column widths. +- Animated transitions on subtitle text change (snap is fine). +- Functional ⌘+S keyboard shortcut — arrives with Phase 3 keymap. The hint is a visual label until then. +- Diff view / form-level "you changed N fields" indicator. +- Light theme. diff --git a/extension/icons/relicario-logo-16.svg b/extension/icons/relicario-logo-16.svg index 09b8e11..ab27a07 100644 --- a/extension/icons/relicario-logo-16.svg +++ b/extension/icons/relicario-logo-16.svg @@ -1,13 +1,13 @@ - - + + - - - + + + @@ -15,13 +15,13 @@ - - + + - + diff --git a/extension/icons/relicario-logo.svg b/extension/icons/relicario-logo.svg index a0dee27..7d78d81 100644 --- a/extension/icons/relicario-logo.svg +++ b/extension/icons/relicario-logo.svg @@ -1,79 +1,93 @@ - - + + - - - + + + - - + + + + + + + + + + + + + + + - + - + - - + + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + - + - + - + - + - +