Repository navigation
Redesign the signup page as a split-screen brand moment - #21
Merged
1 commit merged intoSep 7, 2026
Merged
1 commit merged into
1 commit merged into
Conversation
/register was a bare centered form on ivory, shared with /login, with zero brand presence -- the product's actual front door (no marketing route exists) looked identical to every other unauthenticated screen. Adds: - A split-screen layout (form left, brand panel right, stacking below 768px) via a new `split` variant on the existing AuthLayout -- /login is untouched, still the plain centered column. - SignupBrandPanel: a deliberate one-time reversal of the app's normal ink-on-paper (uses --color-text/--color-surface directly, so it still reads as a reversal in dark mode rather than flattening into it), the serif "Trueup" wordmark on its first public-page appearance, real grounded copy (four model portfolios, monthly rebalancing) instead of generic fintech copy, and the genuine three-step sequence -- create account, verify identity, link bank -- numbered because it actually is one, with the current step marked via the system's sanctioned "current selection" use of the accent color. - A password visibility toggle, via a new optional `labelExtra` slot on the shared Input component (purely additive -- every other caller is unaffected, verified by screenshot). - A segmented password-strength meter driven by the exact same describePassword() the checklist already used -- no new fabricated scale -- as the page's one deliberate interactive/animated moment, respecting the app's existing global reduced-motion rule. - A real fix for the duplicate-email case: the backend now raises ValidationError(..., code="email_taken") instead of the shared validation_failed code, so the form can show "This account already exists -- log in instead." with a working link, instead of the generic "check your details" message the dead-code fallback string was silently never reaching. Deliberately not added: a Terms-of-Service checkbox (no such page/legal content exists yet in the app -- would mean inventing legal copy) and any specific regulatory disclosure claims (kept the one trust line to something true by construction). Verified: backend 24/24 auth tests pass (including the updated duplicate-email assertion); npm run lint and build clean; full manual pass in the browser -- real registration through the running local backend, the duplicate-email path end to end with the new message and link, password toggle and strength meter both interactive, light and dark themes (dark mode's inversion checked explicitly, panel correctly flips light-on-dark), mobile width (375px, panel correctly hidden, single column), and a regression screenshot confirming /login (which doesn't use the new labelExtra prop) is pixel-identical to before. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
/registerwas a bare centered form on ivory, shared with/login, with zero brand presence — the product's actual front door (no marketing route exists) looked identical to every other unauthenticated screen. Two real functional gaps came with it, not just visual flatness:Implementation
splitvariant on the existingAuthLayout—/loginis untouched, still the plain centered column.SignupBrandPanel: a deliberate one-time reversal of the app's normal ink-on-paper (uses--color-text/--color-surfacedirectly, so it still reads as a reversal in dark mode rather than flattening into it), the serif "Trueup" wordmark on its first public-page appearance, real grounded copy (four model portfolios, monthly rebalancing) instead of generic fintech copy, and the genuine three-step sequence — create account, verify identity, link bank — numbered because it actually is one, with the current step marked via the design system's own sanctioned "current selection" use of the accent color.labelExtraslot on the sharedInputcomponent (purely additive — every other caller unaffected, verified by screenshot).describePassword()the checklist already used — no new fabricated scale — as the page's one deliberate interactive/animated moment, respecting the app's existing global reduced-motion rule.ValidationError(..., code="email_taken")instead of the sharedvalidation_failedcode, so the form shows "This account already exists — log in instead." with a working link.Deliberately not added: a Terms-of-Service checkbox (no such page/legal content exists yet in the app — would mean inventing legal copy) and any specific regulatory disclosure claims (kept the one trust line to something true by construction).
Validation
tests/api/test_auth.py24/24 pass, including the updated duplicate-email assertion.npm run lintandnpm run buildclean./login— which doesn't use the newlabelExtraprop — is pixel-identical to before.Security / DB / API impact
codefield more specific.POST /auth/register's duplicate-email response gains a distinctcode(email_takeninstead ofvalidation_failed); the response shape and status (422) are unchanged.🤖 Generated with Claude Code