ui: collapsible settings sections + welcome-email + larger auth inputs
Settings page tidy-up driven by user feedback that it had grown too busy:
- Each section (Import, Invite, Email digests, Cloud sync) is now a
native <details>/<summary> accordion. Import stays open by default
because /settings#import is the deep-link target from the dashboard
CTA; the others collapse so the page lands quiet.
- Manage subscription is a right-aligned gear-icon button instead of
a rectangular text button — the descriptive copy moves into the
tooltip. Frees up the Tier row of visual weight.
Auth + modal inputs were too small (verify code box, portfolio restore
PIN): the auth-card selector now covers text inputs as well, and a new
.modal-input class standardises 16px / 12px-padding fields used in the
cloud-sync enable modal and the portfolio restore prompt.
The verify page no longer carries the "Email me the digest" checkbox —
it was misleading on repeat logins (server-side it only applied on
first sign-up but rendered every time). Default-opt-in lives in the
User row at creation; per-user changes happen on /settings. First
successful verify now triggers a one-shot welcome email explaining the
digest cadence and pointing at /settings for opt-out; SMTP failure is
logged but does not block the login.
Tests rewritten to cover the new welcome-email path:
- first login sends exactly one welcome email
- returning user gets none
- SMTP failure does not break the redirect
- regression guard: returning user who opted out stays opted out
Also lands the paddle merchant-summary doc that was written earlier
during the Paddle → Polar → Stripe onboarding pivot.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
a07fd144ea
commit
00211fec02
8 changed files with 553 additions and 124 deletions
|
|
@ -566,6 +566,53 @@ table.dense tr.row-stale td { color: var(--dim); }
|
|||
.pf-actions button:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
.pf-actions .pf-secondary { color: var(--muted); }
|
||||
.pf-actions .pf-secondary:hover { color: var(--negative); border-color: var(--negative); }
|
||||
|
||||
/* Settings-page action button — same visual language as .pf-actions
|
||||
button so buttons across /settings (Manage subscription, future
|
||||
actions) read as one family. Standalone class (not nested under a
|
||||
parent) so it can be dropped onto any button anywhere on the page. */
|
||||
.settings-btn {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
background: var(--surface-2);
|
||||
color: var(--accent);
|
||||
border: 1px solid var(--border);
|
||||
padding: 7px 14px;
|
||||
cursor: pointer;
|
||||
border-radius: 2px;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
}
|
||||
.settings-btn:hover { border-color: var(--accent); }
|
||||
.settings-btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
|
||||
/* Icon-button variant for inline row actions (e.g. Manage subscription
|
||||
gear in the Tier row). Square hit area, accent on hover, tooltip via
|
||||
title attribute. */
|
||||
.settings-icon-btn {
|
||||
background: transparent;
|
||||
border: 1px solid transparent;
|
||||
color: var(--muted);
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
padding: 0;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
border-radius: 3px;
|
||||
flex-shrink: 0;
|
||||
transition: color 80ms linear, border-color 80ms linear, background 80ms linear;
|
||||
}
|
||||
.settings-icon-btn:hover {
|
||||
color: var(--accent);
|
||||
border-color: var(--border);
|
||||
background: var(--surface-2);
|
||||
}
|
||||
.settings-icon-btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
.settings-icon-btn svg { display: block; }
|
||||
.pf-analysis {
|
||||
margin-top: 14px;
|
||||
background: var(--surface-2);
|
||||
|
|
@ -859,16 +906,46 @@ details[open] .pf-analysis__head-left::before { content: "▾ "; }
|
|||
letter-spacing: 0.06em;
|
||||
gap: 4px;
|
||||
}
|
||||
.auth-card input[type="email"], .auth-card input[type="password"] {
|
||||
.auth-card input[type="email"],
|
||||
.auth-card input[type="password"],
|
||||
.auth-card input[type="text"] {
|
||||
background: var(--bg);
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 13px;
|
||||
padding: 8px 10px;
|
||||
font-size: 16px;
|
||||
padding: 12px 14px;
|
||||
outline: none;
|
||||
border-radius: 3px;
|
||||
}
|
||||
/* The 6-digit OTP input wants to be visually loud — it's the only
|
||||
thing the user is doing on that page. Bigger, more spacing, taller. */
|
||||
.auth-card input[name="code"] {
|
||||
font-size: 24px;
|
||||
padding: 16px 14px;
|
||||
letter-spacing: 0.5em;
|
||||
text-align: center;
|
||||
}
|
||||
.auth-card input:focus { border-color: var(--accent); }
|
||||
|
||||
/* --- Modal text inputs (cloud-sync PIN modal, etc.) ---------------- */
|
||||
/* Same visual treatment as auth-card so prompts read as a coherent
|
||||
family. Replaces the inline `style="padding:8px"` that left these
|
||||
inputs feeling cramped. */
|
||||
.modal-input {
|
||||
width: 100%;
|
||||
background: var(--bg);
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 16px;
|
||||
padding: 12px 14px;
|
||||
margin-bottom: 12px;
|
||||
outline: none;
|
||||
border-radius: 3px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.modal-input:focus { border-color: var(--accent); }
|
||||
.auth-card button {
|
||||
margin-top: 8px;
|
||||
background: transparent;
|
||||
|
|
@ -943,7 +1020,13 @@ details[open] .pf-analysis__head-left::before { content: "▾ "; }
|
|||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.settings-section { margin-top: 22px; }
|
||||
/* Sections are <details> elements — collapsed by default to keep the
|
||||
settings page scannable. Click the summary to expand. */
|
||||
.settings-section {
|
||||
margin-top: 14px;
|
||||
border-top: 1px solid var(--surface-2);
|
||||
padding-top: 14px;
|
||||
}
|
||||
.settings-section__head {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 11px;
|
||||
|
|
@ -951,8 +1034,30 @@ details[open] .pf-analysis__head-left::before { content: "▾ "; }
|
|||
text-transform: uppercase;
|
||||
color: var(--accent);
|
||||
margin-bottom: 6px;
|
||||
cursor: pointer;
|
||||
list-style: none;
|
||||
user-select: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 4px 0;
|
||||
}
|
||||
.settings-section__head::before { content: "▸ "; color: var(--accent); }
|
||||
/* Suppress the native disclosure marker (Webkit + Firefox). */
|
||||
.settings-section__head::-webkit-details-marker { display: none; }
|
||||
.settings-section__head::marker { content: ""; }
|
||||
.settings-section__head::before {
|
||||
content: "▸";
|
||||
color: var(--accent);
|
||||
display: inline-block;
|
||||
transition: transform 120ms ease-out;
|
||||
font-size: 10px;
|
||||
}
|
||||
.settings-section[open] > .settings-section__head::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
.settings-section[open] > .settings-section__head { margin-bottom: 10px; }
|
||||
.settings-section__head:hover { color: var(--text); }
|
||||
.settings-section__head:hover::before { color: var(--text); }
|
||||
.settings-section__lede {
|
||||
color: var(--muted);
|
||||
font-size: 12.5px;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue