# Phase 2B: Polish Foundation + Form Layout **Date:** 2026-05-02 **Status:** Spec, awaiting review **Surface:** Browser extension — popup, fullscreen vault, setup wizard **Parent spec:** `docs/superpowers/specs/2026-04-30-relicario-fullscreen-ux-redesign-design.md` ## Goal 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 - 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. ## Visual language 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: ``` ┌────────────────────────────────────────────────────────────┐ │ edit login ⌘+S to save │ │ unsaved · esc to cancel │ ├──────────────────────────┬─────────────────────────────────┤ │ [ glass: IDENTITY ] │ [ glass: CREDENTIALS ] │ │ title [required] │ username │ │ url + ⤓ │ password ⊙ ↻ │ │ group (autocomplete) │ strength: ████░ │ │ │ totp secret ◫ │ │ │ live: 492 837 · 23s │ ├──────────────────────────┴─────────────────────────────────┤ │ NOTES │ │ ▾ custom sections ▸ attachments │ ├────────────────────────────────────────────────────────────┤ │ STICKY SAVE BAR [cancel] [save] │ └────────────────────────────────────────────────────────────┘ ``` ### 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). - 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 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 ### Sticky save bar - `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. ### Header treatment - 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). ### Other item types 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/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). 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. **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. 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.). ## Out of scope / deferred - 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.