Sign in
Sign in, sign up and password reset in one card, with password show and hide, validation, a one-time code step, a magic link and a passkey button.
The account screens most products need on day one, in one file. Sign in and Create account are tabs; Forgot password, the one-time code after sign-up, the "check your email" step and the signed-in state are views of the same card. On wide screens a brand panel with a customer quote sits beside the form; on narrower ones the form stands alone. Everything validates, and the test code for the verification step is shown under the field.
Components
Behavior
- Sign in. Email and password are required; the password has a show and hide button. "Keep me signed in" is a plain checkbox you can wire to your session length.
- Create account. Name, email, a password with three rules that tick as you type (8 characters, a letter and a number, not your email) and the terms checkbox. Submitting goes to the code step.
- One-time code. Six cells that take typing, Backspace and a pasted code. A short code and a wrong code get different errors. "Send a new code" waits 30 seconds between sends and says so on the button.
- Forgot password. It asks for the email and then answers the same way whether or not the account exists, so the form can't be used to find out who has an account.
- Magic link and passkey. The two buttons under "or" stand in for passwordless sign-in. They use generic icons on purpose: if you add Google or Apple sign-in, follow each provider's own button guidelines.
Accessibility
- Every step change moves focus to the card heading, so screen reader users hear "Check your email" or "Reset your password" instead of silence. Switching tabs leaves focus on the tab, as the tabs pattern expects.
- A failed submit moves focus to the first field with an error; the error is linked to the field and read with it.
- The password rules are said once in the field's description. The ticking checklist under it is visual only (
aria-hidden), so it doesn't repeat itself on every keystroke. - The show-password button changes its name ("Show password", "Hide password") instead of using
aria-pressed. Each code cell after the first is named "Digit 2 of 6" and so on. - The resend button uses
aria-disabledwhile it counts down, so it keeps focus and stays in the Tab order. autocompleteis set on every field (username,current-password,new-password,one-time-code), so password managers and SMS code autofill work.
Use the code
The Code tab shows the whole screen as one file, with @camaleo/react and lucide-react as its only dependencies. The wait function stands in for your auth server: replace it with your requests, and replace DEMO_CODE with a check on the server. Never decide on the client whether a code or a password is right.