docs(spec): generator UX redesign — inline panel + ✨ trigger
Replaces the right-anchored popover (which clips off the popup edge) with an inline panel that injects into the form below the password row. Trigger becomes a ✨ icon button (gold-bg). "save default" demoted to secondary link; single gold "use" CTA. Bundles label-casing polish (drop CAPS LOCK, gold required marker) since .label is shared. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,142 @@
|
|||||||
|
# Generator UX redesign + adjacent popup polish — design
|
||||||
|
|
||||||
|
**Date:** 2026-04-24
|
||||||
|
**Scope:** Replace the right-anchored popover that opens from the password generator trigger with an inline panel that lives inside the form. Swap the "gen" text button for a ✨ icon button. Tighten the label/affordance treatment in the touched screens (login form + vault settings) along the way. Backgrounds, palette, and other unrelated UI stay untouched.
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
The current popover (β₂, commit `8a16482`) positions itself by anchoring its left edge to the trigger button's left edge, but the trigger sits on the right side of the password input row. Combined with the popover's `min-width: 300px` inside a 360 px Chrome popup, the popover always overflows the popup boundary by ~180–220 px. In manual testing it appears as a clipped card with cut-off labels and inaccessible buttons.
|
||||||
|
|
||||||
|
A surgical clamp-fix (~10 lines) would patch the symptom but leave the underlying UX awkward — even when fully visible, the popover floats over the form, hides what you were filling out, and crams two primary actions ("save default" + "use this value") next to each other. The user's feedback was explicit: "we may gotta plan some ui overhauls here, like an emoji instead of 'gen' and a cleaner UI approach for sure." This redesign replaces the popover pattern entirely instead of patching it.
|
||||||
|
|
||||||
|
## Visual identity
|
||||||
|
|
||||||
|
### Trigger button
|
||||||
|
|
||||||
|
- **Icon:** ✨ (U+2728 sparkles emoji). Reads as "auto-generate / freshly minted." Visually rhymes with the sparkle dot on the new logo's gem (commit `a3f13fd`).
|
||||||
|
- **Color:** deep gold `#7c5719` background, `#fff3cf` text — matches primary-button styling from the palette refresh.
|
||||||
|
- **Hover state:** background `#aa812a` (mid gold).
|
||||||
|
- **Active state** (panel open): background `#aa812a` (visually distinct from idle so the user can tell at a glance whether the panel is open).
|
||||||
|
- **Layout:** stays in the existing `.inline-row` pattern next to the password input; replaces the current `<button class="btn" id="gen-btn">gen</button>` with `<button class="gen-trigger" id="gen-btn" aria-expanded="false">✨</button>`.
|
||||||
|
- **Tooltip:** `title="generate password"` for hover.
|
||||||
|
- **Width:** ~38 px (single emoji glyph fits without padding noise).
|
||||||
|
|
||||||
|
### Inline panel (replaces popover)
|
||||||
|
|
||||||
|
When ✨ is clicked, a panel injects into the form's DOM **between the password row and the next form-group** (e.g., the totp-secret row). Other fields below shift down. The panel:
|
||||||
|
|
||||||
|
- Lives at the form's full available width (no positioning math, no clipping).
|
||||||
|
- Has a subtle gold border (`1px solid #aa812a`) to feel attached to the trigger.
|
||||||
|
- Auto-generates a preview the moment it opens, using `VaultSettings.generator_defaults` as the initial knob state.
|
||||||
|
|
||||||
|
Panel composition (top to bottom):
|
||||||
|
|
||||||
|
1. **Kind toggle** — pill-style two-button switch: `random` / `passphrase`. Active button: gold-bg.
|
||||||
|
2. **Common knobs (always visible):**
|
||||||
|
- For `random`: length slider (8–48, default 20), four character-class checkboxes (a-z / A-Z / 0-9 / !@#).
|
||||||
|
- For `passphrase` (BIP39): word_count slider (3–10, default 4), separator text input (1 char), capitalization radio (lower / upper / title).
|
||||||
|
3. **Preview row** — generated value in monospace gold (`#f1cf6e`), with a `↻` regenerate button.
|
||||||
|
4. **`more ▾` disclosure** — when expanded, shows the rarely-used knobs:
|
||||||
|
- For `random`: symbol charset (`safe` / `full` toggle).
|
||||||
|
- For `passphrase`: nothing extra (separator and capitalization moved to common).
|
||||||
|
- For both: an empty placeholder when no advanced knobs apply (so the disclosure always renders for consistency, even if collapsed-only).
|
||||||
|
5. **Action row:**
|
||||||
|
- **`↑ save these as default`** — small underlined link, left-aligned, `#8b949e` color → `#d2ab43` on hover. Writes current knobs to `VaultSettings.generator_defaults` via the existing `update_vault_settings` message; shows a brief "saved" toast next to the link; panel stays open. **Demoted from primary button** because most of the time the user just wants this password, not to change global defaults.
|
||||||
|
- **`cancel`** — secondary button (transparent bg, gray border).
|
||||||
|
- **`use`** — primary CTA: gold bg `#7c5719`, `#fff3cf` text. Commits the current preview value into the password input and closes the panel.
|
||||||
|
|
||||||
|
### Adjacent polish (scope B)
|
||||||
|
|
||||||
|
Touched only in screens we're already modifying (login form + vault settings):
|
||||||
|
|
||||||
|
- **Form labels:** `.label` class drops `text-transform: uppercase` and reduces `letter-spacing` from `0.5px` to `0.02em`. Lowercase labels match the panel's knob labels and feel less shouty. Font weight goes 600 → 500 for slightly less visual weight; color stays `#8b949e`.
|
||||||
|
- **Required marker:** the existing `*` next to required-field labels picks up gold (`#aa812a`) instead of inheriting label gray, so it actually reads as a marker.
|
||||||
|
- **Button styles:** primary form buttons (cancel/save at the bottom of the login form) already use the palette refresh; nothing to change there.
|
||||||
|
|
||||||
|
These polish changes apply to ALL form labels in the login form and vault settings (not just the password row), since the `.label` class is shared. Other forms that use `.label` (SecureNote, Identity, Card, Key, Totp, Document-coming-soon) will pick up the lowercase treatment automatically — that's a deliberate choice, not a side effect: the CAPS LOCK feel was a project-wide rough edge that's worth fixing in this slice.
|
||||||
|
|
||||||
|
## Behavior
|
||||||
|
|
||||||
|
| Trigger | Action |
|
||||||
|
|---------|--------|
|
||||||
|
| click ✨ | toggle panel open/closed; auto-generate on first open using saved defaults |
|
||||||
|
| click ↻ | regenerate preview (no commit) |
|
||||||
|
| change a knob | debounced auto-regenerate (150 ms — same as existing) |
|
||||||
|
| click `use` | commit current preview into password field, close panel |
|
||||||
|
| click `cancel` | close panel without committing; password field unchanged |
|
||||||
|
| click `↑ save these as default` | write current knobs to `VaultSettings.generator_defaults`; show toast; panel stays open |
|
||||||
|
| press Escape (when panel open) | close panel without committing |
|
||||||
|
| click ✨ again while panel open | close panel (no commit) |
|
||||||
|
|
||||||
|
The panel does NOT close on click-outside. The user might want to drag from the panel to verify the value or copy it before clicking `use`; closing on click-outside makes that fragile. Escape and explicit cancel/use are the dismissal paths.
|
||||||
|
|
||||||
|
## Vault settings adaptation
|
||||||
|
|
||||||
|
The vault settings screen currently has a `<button id="configure-gen">configure ▾</button>` next to a generator-summary text line. After redesign:
|
||||||
|
|
||||||
|
- The "configure ▾" button becomes a ✨ button matching the login form trigger.
|
||||||
|
- When clicked, the same inline panel renders **inside the vault-settings "generator" section** (not as a popover).
|
||||||
|
- One difference from the login-form context: the action row drops the `cancel` and `use` buttons since there's no password input to fill — instead, the panel is purely for inspecting/configuring defaults. The `↑ save these as default` link becomes the only action in this context, and ✨ closes the panel just like in the login form.
|
||||||
|
- The generator preview text line (`generatorSummary(...)`) stays above the panel even when expanded — it serves as a "current default" reference.
|
||||||
|
|
||||||
|
## Files affected
|
||||||
|
|
||||||
|
### Modified
|
||||||
|
|
||||||
|
- **`extension/src/popup/components/generator-popover.ts`** — major rewrite. Probably gets renamed to `generator-panel.ts` (cleaner semantics). Same module, different positioning (inline DOM injection vs absolute-positioned popover) and different action set per context.
|
||||||
|
- **`extension/src/popup/components/types/login.ts`** — replace `gen-btn` text content with ✨; update click handler to call the renamed module; drop the standalone close-on-blur logic if any.
|
||||||
|
- **`extension/src/popup/components/settings-vault.ts`** — replace `configure-gen` button content with ✨; update click handler; render the inline panel in place rather than calling the popover open.
|
||||||
|
- **`extension/src/popup/styles.css`** — add `.gen-trigger` rule (button styling); add `.gen-panel` and child rules (replacing `.generator-popover` rules). Modify `.label` rule to drop uppercase and tighten letter-spacing/weight; modify `.label .req` (or equivalent for the `*`) to gold. Remove the `.generator-popover` rules entirely once the new panel works (no need to keep old popover CSS around).
|
||||||
|
|
||||||
|
### Renamed
|
||||||
|
|
||||||
|
- `extension/src/popup/components/generator-popover.ts` → `extension/src/popup/components/generator-panel.ts`. Test file follows: `__tests__/generator-popover.test.ts` → `__tests__/generator-panel.test.ts`. Update imports in `login.ts`, `settings-vault.ts`, and the test file accordingly. Sequencing decision (git-mv first vs rewrite first) noted in open questions.
|
||||||
|
|
||||||
|
### Updated tests
|
||||||
|
|
||||||
|
- **`extension/src/popup/components/__tests__/generator-popover.test.ts`** (renamed): existing 7 tests cover knob → message-shape behavior. Most should survive verbatim — they're DOM-level, not positioning-level. Update test setup to mount the panel inline (in a parent container) rather than asserting on `document.body` children. Add 2–3 new tests:
|
||||||
|
- Panel opens via aria-expanded toggling on the trigger
|
||||||
|
- Panel auto-generates on first open
|
||||||
|
- Escape key closes the panel
|
||||||
|
|
||||||
|
### Markup unchanged but new selectors
|
||||||
|
|
||||||
|
- The `.inline-row` pattern in login form stays. Just the button content/styling changes.
|
||||||
|
|
||||||
|
## Acceptance
|
||||||
|
|
||||||
|
- [ ] Clicking ✨ on the login form opens an inline panel below the password row.
|
||||||
|
- [ ] Panel auto-generates a preview using current `VaultSettings.generator_defaults`.
|
||||||
|
- [ ] Knob changes debounce-regenerate; ↻ button forces a regenerate.
|
||||||
|
- [ ] `use` button commits preview into password input and closes panel.
|
||||||
|
- [ ] `cancel` button closes panel without committing.
|
||||||
|
- [ ] Escape key closes panel without committing.
|
||||||
|
- [ ] Clicking ✨ again while panel open closes it.
|
||||||
|
- [ ] `↑ save these as default` link writes to `VaultSettings.generator_defaults`; toast appears; panel stays open.
|
||||||
|
- [ ] Vault settings ✨ button opens the same panel inline (no popover); `↑ save these as default` is the only action; ✨ toggles closed.
|
||||||
|
- [ ] All form labels in login + vault settings are lowercase with reduced letter-spacing.
|
||||||
|
- [ ] Required-field `*` marker is gold (`#aa812a`).
|
||||||
|
- [ ] No element overflows the popup right edge in any state.
|
||||||
|
- [ ] `bun run test` passes (existing 7 generator tests survive the rename + 2-3 new tests added → ~9–10 generator-panel tests; total still around 124–127).
|
||||||
|
- [ ] `bunx tsc --noEmit` clean.
|
||||||
|
- [ ] `bun run build:all` clean (Chrome + Firefox).
|
||||||
|
- [ ] No new automated tests for the visual polish (label casing, gold `*`) — visually verified.
|
||||||
|
- [ ] Manual: walk through both contexts (login form + vault settings) on Chrome and Firefox.
|
||||||
|
|
||||||
|
## Out of scope
|
||||||
|
|
||||||
|
- The capture-prompt and ack-prompt content scripts (still use their own button styling — no change here).
|
||||||
|
- The setup tab's strength-bar / advice-block (touched in logo-refresh palette swap; nothing more to do).
|
||||||
|
- Other popup forms beyond their `.label` class picking up the lowercase treatment automatically (no per-type form rework).
|
||||||
|
- Generator output strength visualization (zxcvbn meter inside the panel) — could be a future polish but not now.
|
||||||
|
- Multi-preview / "show 3 candidates" pattern — keeping the single-preview + regenerate flow.
|
||||||
|
- Animation/transitions on panel open-close — purely instant for now (a fade or slide-down can be added later as polish without breaking anything).
|
||||||
|
- Click-outside-to-close — explicitly NOT included (see Behavior section reasoning).
|
||||||
|
|
||||||
|
## Open questions deferred to plan
|
||||||
|
|
||||||
|
- **Module rename ordering:** is it cleaner to (a) rewrite in-place keeping the `generator-popover.ts` filename then rename in a follow-up, or (b) git-mv first then rewrite? Plan ships (b) — git-mv preserves history, reviewers see "rename + edits" cleanly.
|
||||||
|
- **Test mounting strategy:** existing tests `document.body.appendChild(host)` then assert. New panel mounts inside a parent. Plan: tests create a parent div, pass it as the mount target to a new `openGeneratorPanel(opts)` signature that takes `{ parent, anchor, initial, onPicked, onCancel }`. The login-form caller passes the form element as `parent`.
|
||||||
|
- **The "more ▾" placeholder:** for passphrase mode, all knobs are common and there's nothing in advanced. Plan: render the disclosure with text "(no advanced options for passphrase)" when expanded, OR hide the disclosure entirely in passphrase mode. Plan ships the hide-when-empty option — less visual noise.
|
||||||
|
- **`save default` toast:** existing toast infrastructure in popup? If yes, reuse. If not, the smallest toast = a 1.5s fade-in/fade-out span next to the `↑ save these as default` link saying "✓ saved". Plan picks based on what already exists.
|
||||||
|
- **Vault-settings panel ✨ — when no defaults exist:** the very first time a vault is created, `VaultSettings.generator_defaults` should already be initialized (it is, per β₂). Confirm and document.
|
||||||
Reference in New Issue
Block a user