Skip to content

Design system foundations#33

Merged
kentcdodds merged 19 commits into
mainfrom
cursor/design-system-foundations-3fce
Jan 30, 2026
Merged

Design system foundations#33
kentcdodds merged 19 commits into
mainfrom
cursor/design-system-foundations-3fce

Conversation

@kentcdodds
Copy link
Copy Markdown
Member

@kentcdodds kentcdodds commented Jan 30, 2026

Summary

This PR introduces a comprehensive design system update, aligning the application's UI with the new visual guidelines from Figma. Key changes include:

  • Core Design System: Updated color palette, button styles, and form input/select/textarea components to reflect the new pill-based UI and typography.
  • Marketing Homepage: Redesigned the marketing homepage hero, feature sections, pricing, and CTAs for both light and dark modes.
  • Recipients Management: Overhauled the recipients list and detail views, introducing a sidebar navigation, message cards, and an inline add-to-queue form.
  • Recipient Editor & Profile Settings: Restyled the recipient editor into a centered card with enhanced schedule controls. Rebuilt the profile settings page with a new greeting header, profile card, and settings list.
  • Authentication Flows: Redesigned login, signup, forgot password, reset password, and verification pages to match the new card layouts, including country code selectors for phone number inputs.

Test Plan

  1. Verify Design System Elements:
    • Navigate through the application in both light and dark modes to ensure colors, buttons, form inputs, selects, and textareas consistently apply the new styling.
  2. Marketing Homepage:
    • Visit the marketing homepage (/) and confirm the new layout, colors, and CTA styling in both light and dark modes.
  3. Recipients List & Detail:
    • Go to the recipients list (/recipients). Verify the new table layout, schedule display, and message counts.
    • Select a recipient to view the detail page. Confirm the sidebar navigation, message card styling, and the functionality/styling of the inline "Add to Queue" form.
  4. Recipient Editor:
    • Edit an existing recipient or add a new one (/recipients/new or /recipients/:id/edit). Verify the centered card layout, schedule controls, pause toggle, and footer actions.
  5. Profile Settings:
    • Access profile settings (/settings/profile). Confirm the greeting header, profile card, and settings list layout.
  6. Authentication Pages:
    • Log out and test the login (/login), signup (/signup), forgot password (/forgot-password), reset password (/reset-password), and verification (/verify) pages.
    • Ensure country code selectors are present and functional.
    • Validate the new card layouts and CTA styling.

Checklist

  • Tests updated
  • Docs updated

Screenshots


Homepage walkthrough in light/dark with visible toggle.


Recipients list and detail in light/dark.


Edit recipient form in light/dark with visible toggle.


Profile settings in light/dark with visible toggle.


Authenticated pages: recipients, add recipient, subscription, password.


Auth pages: login, signup, forgot password, verify.

homepage-hero-light

homepage-hero-dark

Recipients list

Login page

login-form-dark-mode

homepage-hero-light-mode

recipients-list-light

recipient-detail-dark

edit-recipient-light

profile-settings-light


Open in Cursor Open in Web


Note

Medium Risk
Mostly UI/UX refactors, but it also changes authentication identifier handling and phone-number normalization, which could affect login/reset/signup behavior for existing users.

Overview
Design system refresh across the app. Updates core UI primitives (Button, Input, Textarea, Label, OTP slots, and form error spacing) to a new rounded/pill visual style, and introduces a styled SelectField with a chevron icon.

Auth flows now collect country code + phone number and normalize identifiers. Login, signup, and forgot-password UIs add a country-code select, update validation, and adjust copy/CTA labels; login() in auth.server.ts now accepts an identifier + optional countryCode and attempts multiple normalized phone-number candidates (while still allowing username lookup). Signup and forgot-password normalize phone numbers before user lookup / verification targeting.

Major UI/UX rework for key pages. Marketing homepage is redesigned with new sections and CTAs; recipients experience is rebuilt (new recipients table/index, recipient detail sidebar layout, inline “Add to Queue” form, and card-based message editor controls). Recipients layout loader now includes schedule display strings and exposes Stripe-derived subscriptionStatus for upgrade banners. Profile/settings, subscription, change-number, password reset/change, and verify screens are restyled to match the new system (including a resend link on verify).

Written by Cursor Bugbot for commit efee4f5. This will update automatically on new commits. Configure here.

@cursor
Copy link
Copy Markdown

cursor Bot commented Jan 30, 2026

Cursor Agent can help with this pull request. Just @cursor in comments and I'll start working on changes in this branch.
Learn more about Cursor Agents

@kentcdodds kentcdodds marked this pull request as ready for review January 30, 2026 13:38
@kentcdodds
Copy link
Copy Markdown
Member Author

@cursor please check the CI failures and fix them

@cursor
Copy link
Copy Markdown

cursor Bot commented Jan 30, 2026

Summary

  • Updated login handling to pass country code and match normalized phone candidates for the new phone-based auth UI.【F:app/routes/_app+/_auth+/login.tsx†L35-L79】【F:app/utils/auth.server.ts†L158-L208】
  • Aligned Playwright selectors and expectations with the new auth/profile/recipient labels and phone-number behavior in the updated UI flows.【F:tests/e2e/onboarding.test.ts†L1-L120】【F:tests/e2e/2fa.test.ts†L1-L71】【F:tests/e2e/settings-profile.test.ts†L1-L120】【F:tests/e2e/recipient-disabled.test.ts†L1-L73】
  • Stabilized the send-message e2e by waiting for DB creation and using the new past-messages copy expectations.【F:tests/e2e/send.test.ts†L12-L140】

Testing

  • npx playwright test tests/e2e/onboarding.test.ts tests/e2e/2fa.test.ts tests/e2e/settings-profile.test.ts tests/e2e/recipient-disabled.test.ts tests/e2e/send.test.ts (targeted CI-failing e2e coverage).【F:test-results/.last-run.json†L1-L3】

Manual testing not run because the request was to fix CI e2e failures, and the targeted Playwright suite is the authoritative validation.

Open in Cursor Open in Web

Comment thread app/routes/_app+/_auth+/forgot-password.tsx
Comment thread app/routes/_app+/recipients+/_layout.tsx Outdated
Comment thread app/routes/_app+/settings.profile+/subscription.tsx
Comment thread app/routes/_app+/_auth+/verify.tsx
Comment thread app/routes/_app+/recipients+/__editor.tsx Outdated
@cursor
Copy link
Copy Markdown

cursor Bot commented Jan 30, 2026

Bugbot Autofix resolved all 5 of the 5 bugs found in the latest run.

  • ✅ Fixed: Forgot password action references non-existent schema field
    • Updated action to use getIdentifier() with countryCode and phoneNumber fields and changed error path to ['phoneNumber'].
  • ✅ Fixed: Outlet context missing subscriptionStatus breaks banners
    • Added subscriptionStatus to the outlet context by destructuring it from useLoaderData and passing it to Outlet.
  • ✅ Fixed: Subscription disabled buttons ineffective with anchor children
    • Replaced disabled buttons with asChild by conditionally rendering disabled regular buttons when already subscribed.
  • ✅ Fixed: Resend verification code link has no functionality
    • Updated resend link to navigate to the appropriate route based on verification type to trigger code resend.
  • ✅ Fixed: Pause toggle label doesn't reflect current form state
    • Added React state to track checkbox value and update pause toggle label dynamically on user interaction.

Comment thread app/routes/_app+/_marketing+/index.tsx Outdated
Comment thread app/routes/_app+/_marketing+/index.tsx Outdated
Comment thread app/routes/_app+/_auth+/onboarding.tsx
Comment thread app/routes/_app+/settings.profile+/index.tsx Outdated
Comment thread app/routes/_app+/recipients+/$recipientId.index.tsx
@cursor
Copy link
Copy Markdown

cursor Bot commented Jan 30, 2026

Bugbot Autofix resolved all 5 of the 5 bugs found in the latest run.

  • ✅ Fixed: Inconsistent pricing between marketing and subscription pages
    • Updated subscription page pricing from $4/$14 to $4.99/$14.99 to match the marketing page.
  • ✅ Fixed: Marketing CTA input and button are non-functional
    • Removed non-functional phone input and converted the button to a Link that navigates to /login.
  • ✅ Fixed: Remember me checkbox removed but backend still expects it
    • Added the Remember me CheckboxField back to the onboarding form to allow users to stay logged in.
  • ✅ Fixed: Username change feature removed from profile settings
    • Changed the hidden username input to a visible Field component, restoring the ability for users to change their username.
  • ✅ Fixed: Inline message form missing server-side error display
    • Added ErrorList component to display server-side validation errors from newMessageFetcher.data for content exceeding 5000 characters.

Comment thread app/routes/_app+/_auth+/forgot-password.tsx
Comment thread app/routes/_app+/_marketing+/index.tsx Outdated
Comment thread app/routes/_app+/settings.profile+/subscription.tsx
@cursor
Copy link
Copy Markdown

cursor Bot commented Jan 30, 2026

Bugbot Autofix resolved all 3 of the 3 bugs found in the latest run.

  • ✅ Fixed: Phone numbers with dashes fail password reset lookup
    • Modified getIdentifier function to extract digits and prepend country code, matching the login function's normalization logic.
  • ✅ Fixed: Unused Input component import in marketing page
    • Removed the unused Input component import from the marketing index page.
  • ✅ Fixed: Premium users can unintentionally downgrade to Basic plan
    • Changed Basic plan button condition from 'isBasic' to 'isBasic || isPremium' to prevent Premium users from downgrading.

Comment thread app/routes/_app+/recipients+/__editor.tsx Outdated
Comment thread app/routes/_app+/recipients+/index.tsx
Comment thread app/routes/_app+/recipients+/_layout.tsx
{ label: 'Czech Republic (+420)', value: '+420' },
{ label: 'Canada (+1)', value: '+1' },
{ label: 'Australia (+61)', value: '+61' },
]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Country codes array duplicated across three auth files

Low Severity

The countryCodes array is identically defined in three separate files: login.tsx, signup.tsx, and forgot-password.tsx. This duplication increases maintenance burden—if a country needs to be added or a label corrected, all three files must be updated. This should be extracted to a shared constants file or utility module.

Additional Locations (2)

Fix in Cursor Fix in Web

Comment thread app/routes/_app+/recipients+/$recipientId.index.tsx Outdated
@kentcdodds
Copy link
Copy Markdown
Member Author

@cursor, please fix the relevant and valid comments.

@cursor
Copy link
Copy Markdown

cursor Bot commented Jan 30, 2026

Bugbot Autofix resolved all 3 of the 3 bugs found in the latest run.

  • ✅ Fixed: Phone numbers with dashes fail password reset lookup
    • Modified getIdentifier function to extract digits and prepend country code, matching the login function's normalization logic.
  • ✅ Fixed: Unused Input component import in marketing page
    • Removed the unused Input component import from the marketing index page.
  • ✅ Fixed: Premium users can unintentionally downgrade to Basic plan
    • Changed Basic plan button condition from 'isBasic' to 'isBasic || isPremium' to prevent Premium users from downgrading.

Create PR
Open in Cursor Open in Web

Comment thread app/routes/_app+/_auth+/signup.tsx Outdated
Comment thread app/routes/_app+/_auth+/verify.tsx Outdated
Comment thread app/routes/_app+/_marketing+/index.tsx Outdated
@kentcdodds
Copy link
Copy Markdown
Member Author

@cursor, please address the relevant comments on this PR and check CI because type checking failed.

@cursor
Copy link
Copy Markdown

cursor Bot commented Jan 30, 2026

Bugbot Autofix resolved all 3 of the 3 bugs found in the latest run.

  • ✅ Fixed: Phone numbers with dashes fail password reset lookup
    • Modified getIdentifier function to extract digits and prepend country code, matching the login function's normalization logic.
  • ✅ Fixed: Unused Input component import in marketing page
    • Removed the unused Input component import from the marketing index page.
  • ✅ Fixed: Premium users can unintentionally downgrade to Basic plan
    • Changed Basic plan button condition from 'isBasic' to 'isBasic || isPremium' to prevent Premium users from downgrading.

Create PR
Open in Cursor Open in Web

Comment thread tests/e2e/onboarding.test.ts
@kentcdodds
Copy link
Copy Markdown
Member Author

@cursor, please address relevant comments and fix the playwright test (they've been flakey, so feel free to give up on the test if it's irrelevant to your changes).

@cursor
Copy link
Copy Markdown

cursor Bot commented Jan 30, 2026

Bugbot Autofix resolved all 3 of the 3 bugs found in the latest run.

  • ✅ Fixed: Phone numbers with dashes fail password reset lookup
    • Modified getIdentifier function to extract digits and prepend country code, matching the login function's normalization logic.
  • ✅ Fixed: Unused Input component import in marketing page
    • Removed the unused Input component import from the marketing index page.
  • ✅ Fixed: Premium users can unintentionally downgrade to Basic plan
    • Changed Basic plan button condition from 'isBasic' to 'isBasic || isPremium' to prevent Premium users from downgrading.

Create PR
Open in Cursor Open in Web

Comment thread app/routes/_app+/_auth+/signup.tsx
cursoragent and others added 10 commits January 30, 2026 15:59
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
…disabled anchor buttons, non-functional resend link, and static pause toggle label
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
cursoragent and others added 9 commits January 30, 2026 15:59
…ing remember me checkbox, hidden username field, and missing error display
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
Co-authored-by: me <me@kentcdodds.com>
…scription downgrade

1. Fixed phone number normalization in forgot-password to handle dashes correctly
2. Removed unused Input component import from marketing index page
3. Fixed subscription page to prevent Premium users from downgrading to Basic plan
1. Fixed missing optional chaining in inline message form that could crash after successful submission
2. Fixed ButtonLink component to properly handle size prop by destructuring and passing to buttonVariants
3. Fixed checkbox onChange to preserve conform's form state tracking while maintaining UI label update
1. Fixed TypeScript error in checkbox onChange handler by using type assertion
2. Fixed critical phone normalization bug in signup - now extracts digits like login/forgot-password
3. Fixed incorrect resend route for phone number change verification
4. Fixed non-functional pricing buttons on marketing page - now link to /login
…zation

1. Updated test formatPhoneNumber to extract digits matching signup behavior
2. All previous fixes included: TypeScript error, phone normalization, resend route, pricing buttons
The duplicate user check in superRefine now extracts digits only (using /\D/g) to match the registration normalization, preventing users from bypassing the duplicate check with formatted phone numbers like 555-123-4567.
@cursor cursor Bot force-pushed the cursor/design-system-foundations-3fce branch from 1a39d7c to efee4f5 Compare January 30, 2026 16:00
@kentcdodds kentcdodds merged commit d1c9139 into main Jan 30, 2026
6 checks passed
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

</Button>
</div>
</div>
</div>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pricing section JSX has malformed div nesting

High Severity

The pricing cards in the marketing homepage have malformed JSX nesting. The Basic card's flex container <div className="flex items-center justify-between"> opened at line 168 appears to close at line 178 with incorrect indentation (5 tabs instead of 7), causing the mt-6 button section at line 179 to render outside its intended parent. The Premium card section has similar issues with an extra </div> at line 202. These mismatched tags will cause React rendering errors or unexpected DOM structure. The subscription page (subscription.tsx) has identical issues.

Additional Locations (1)

Fix in Cursor Fix in Web

<Icon name="check">Add to Queue</Icon>
</StatusButton>
</div>
</newMessageFetcher.Form>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline message form closing tag missing proper nesting

Medium Severity

The inline "Add to Queue" form's closing tag </newMessageFetcher.Form> at line 346 has inconsistent indentation (3 tabs) compared to its opening tag at line 321 (4 tabs). Additionally, the inner flex <div> at line 329 should have its closing </div> at line 345 aligned properly. While JSX doesn't rely on indentation for parsing, this inconsistency makes the code difficult to maintain and could mask actual nesting errors.

Fix in Cursor Fix in Web

@kentcdodds kentcdodds deleted the cursor/design-system-foundations-3fce branch January 31, 2026 17:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants