Real-world demo

Login / Register Switch

Two auth forms share the same page, but only the visible form is active, validated, tabbable, and submitted.

Back to demo gallery

Scenario

A product sign-in page lets existing members log in and new visitors register without navigating away. The app keeps both forms as real HTML, then hides and disables the inactive form so validation and focus stay scoped to the current task.

Try it

  • Submit the empty login form and follow the summary links back to each invalid field.
  • Use Register here to reveal the registration form and move focus to its heading.
  • Enter two different passwords in the registration form to trigger the cross-field confirmation rule.
  • Use Sign in instead and confirm the hidden registration fields are no longer in the Tab order.

Example

The inactive form is hidden and its controls are disabled. A polite status message announces mode changes without moving screen reader users away from the newly focused form heading.

Login form is visible. Registration form is hidden.

Existing member

Sign in to your workspace

Use the email and password for an existing account.

Not registered yet?

Accessibility notes

  • Both forms use native labels, inputs, checkboxes, and submit buttons before JavaScript enhances validation.
  • The inactive panel uses hidden, and its form controls are disabled so they are not tabbable, submitted, or validated.
  • Mode changes clear errors from the form being hidden and move focus to the heading of the newly visible form.
  • The hidden live status announces whether login or registration is visible without adding extra visual noise.
  • The registration form demonstrates a cross-field same-as password confirmation rule with custom error copy.
  • Keyboard behavior remains native: Tab moves through the visible form and summary links, Enter submits, and Space toggles checkboxes.
  • The shared demo CSS keeps motion minimal and respects reduced-motion preferences.

Developer notes

Initialize each form separately. When the mode changes, keep the inactive form in the DOM but clear its errors and disable its controls.

Two validator instances

import { createFormValidator } from "./dist/index.js";
import { createDefaultPreset } from "./dist/presets/default.js";

const loginValidator = createFormValidator(loginForm, createDefaultPreset());
const registerValidator = createFormValidator(registerForm, {
  ...createDefaultPreset(),
  messages: {
    sameAs: "Use the same password in both fields."
  }
});

Switch active form

inactiveValidator.clearErrors();
inactivePanel.hidden = true;

for (const control of inactiveForm.elements) {
  control.disabled = true;
}

activePanel.hidden = false;
activeHeading.focus();