Sign In
Login form patterns with social authentication, two-factor input, and responsive layouts. Accessible and progressively enhanced.
Overview
Sign-in forms are critical user touchpoints that require careful attention to usability, accessibility, and security. These patterns demonstrate best practices using Vanilla Breeze's data-layout attributes and custom elements.
Key features:
data-layoutattributes for layout without wrapper elements<form-field>with validation messages using<output><text-divider>for separating authentication methods<brand-mark>for consistent logo display- Progressive OTP enhancement with
data-type="otp" - Proper
autocompleteattributes for password managers - Password toggle (auto-enhanced by JS)
Simple Card Form
A centered login form using data-layout="cover" on the body for vertical centering, with <layout-card> constraining the width. The form itself uses data-layout="stack" for vertical spacing.
<body data-layout="cover" data-layout-min="100vh" data-layout-padding="l"> <layout-card data-max="narrow" data-padding="l" data-layout-principal> <form action="/auth/login" method="POST" data-layout="stack" data-layout-gap="l"> <header data-layout="stack" data-layout-gap="s"> <h1>Sign in</h1> <p>Welcome back! Please enter your details.</p> </header> <form-field> <label for="email">Email</label> <input type="email" id="email" name="email" required autocomplete="email" placeholder="you@example.com" aria-describedby="email-error"/> <output id="email-error" class="error" for="email" aria-live="polite"> Please enter a valid email address. </output> </form-field> <form-field> <label for="password">Password</label> <input type="password" id="password" name="password" required autocomplete="current-password" placeholder="Enter your password" aria-describedby="password-error"/> <output id="password-error" class="error" for="password" aria-live="polite"> Password is required. </output> </form-field> <fieldset class="minimal" data-layout="cluster" data-layout-justify="between" data-layout-align="center"> <label><input type="checkbox" name="remember"/> Remember me</label> <a href="/auth/forgot-password">Forgot password?</a> </fieldset> <button type="submit">Sign in</button> <footer> <p>Don't have an account? <a href="/auth/register">Sign up</a></p> </footer> </form> </layout-card></body>
Split Screen Layout
A two-column layout using data-layout="split" with a branded panel alongside the form. The <brand-mark> element provides consistent logo display. Collapses to single column on mobile.
<body> <div data-layout="split" data-layout-fill data-layout-gap="none" data-layout-align="stretch" data-layout-nowrap> <!-- Brand Panel --> <aside class="brand-panel" data-layout="cover" data-layout-min="auto" data-layout-padding="xl"> <div data-layout="stack" data-layout-gap="l" data-layout-principal> <brand-mark data-size="xl">Acme Inc</brand-mark> <h2>Welcome back</h2> <p>Sign in to access your dashboard and continue where you left off.</p> </div> </aside> <!-- Form Panel --> <main data-layout="cover" data-layout-min="auto" data-layout-padding="xl"> <div data-layout="center" data-layout-max="narrow" data-layout-principal> <form action="/auth/login" method="POST" data-layout="stack" data-layout-gap="l"> <h1>Sign in to your account</h1> <form-field> <label for="email">Email address</label> <input type="email" id="email" name="email" required autocomplete="email" placeholder="you@company.com" aria-describedby="email-error"/> <output id="email-error" class="error" for="email" aria-live="polite"> Please enter a valid email address. </output> </form-field> <form-field> <label for="password">Password</label> <input type="password" id="password" name="password" required autocomplete="current-password" placeholder="Enter your password" aria-describedby="password-error"/> <output id="password-error" class="error" for="password" aria-live="polite"> Password is required. </output> </form-field> <fieldset class="minimal" data-layout="cluster" data-layout-justify="between" data-layout-align="center"> <label><input type="checkbox" name="remember"/> Remember me</label> <a href="/auth/forgot-password">Forgot password?</a> </fieldset> <button type="submit">Sign in</button> <footer> <p>Don't have an account? <a href="/auth/register">Create one</a></p> </footer> </form> </div> </main> </div></body>
Brand Panel Styles
.brand-panel { background: linear-gradient(135deg, var(--color-interactive) 0%, var(--color-interactive-hover) 100%); color: white;}.brand-panel h2,.brand-panel p { color: inherit;}
With Social Login
Sign-in form with social authentication buttons above traditional email/password fields. Uses the <text-divider> element to visually separate authentication methods.
<body data-layout="cover" data-layout-min="100vh" data-layout-padding="l"> <layout-card data-max="narrow" data-padding="l" data-layout-principal> <div data-layout="stack" data-layout-gap="l"> <header data-layout="stack" data-layout-gap="s"> <h1>Sign in</h1> <p>Choose your preferred sign-in method.</p> </header> <!-- Social Login Buttons --> <div data-layout="stack" data-layout-gap="s"> <button type="button" class="secondary"> <icon-wc name="brand-google"></icon-wc> Continue with Google </button> <button type="button" class="secondary"> <icon-wc name="brand-github"></icon-wc> Continue with GitHub </button> </div> <!-- Divider using text-divider element --> <text-divider>or continue with email</text-divider> <!-- Email/Password Form --> <form action="/auth/login" method="POST" data-layout="stack" data-layout-gap="l"> <form-field> <label for="email">Email</label> <input type="email" id="email" name="email" required autocomplete="email" placeholder="you@example.com" aria-describedby="email-error"/> <output id="email-error" class="error" for="email" aria-live="polite"> Please enter a valid email address. </output> </form-field> <form-field> <label for="password">Password</label> <input type="password" id="password" name="password" required autocomplete="current-password" placeholder="Enter your password" aria-describedby="password-error"/> <output id="password-error" class="error" for="password" aria-live="polite"> Password is required. </output> </form-field> <fieldset class="minimal" data-layout="cluster" data-layout-justify="between" data-layout-align="center"> <label><input type="checkbox" name="remember"/> Remember me</label> <a href="/auth/forgot-password">Forgot password?</a> </fieldset> <button type="submit">Sign in</button> </form> <footer> <p>Don't have an account? <a href="/auth/register">Sign up</a></p> </footer> </div> </layout-card></body>
Two-Factor Authentication
A verification code input using the progressive OTP enhancement. The input uses data-type="otp" and data-length="6" to enable multi-box display with JavaScript, while working as a standard text input without it.
<body data-layout="cover" data-layout-min="100vh" data-layout-padding="l"> <layout-card data-max="narrow" data-padding="l" data-layout-principal> <form action="/auth/verify" method="POST" data-layout="stack" data-layout-gap="l"> <header data-layout="stack" data-layout-gap="s"> <h1>Two-factor authentication</h1> <p>Enter the 6-digit code from your authenticator app.</p> </header> <!-- OTP Input - Progressive Enhancement Pattern --> <form-field data-no-icon> <label for="code">Verification code</label> <input type="text" id="code" name="code" data-type="otp" data-length="6" inputmode="numeric" pattern="[0-9]{6}" autocomplete="one-time-code" required aria-describedby="code-msg"/> <output id="code-msg" for="code" aria-live="polite"> Enter the 6-digit code from your authenticator app </output> </form-field> <button type="submit">Verify</button> <footer data-layout="stack" data-layout-gap="s"> <p>Didn't receive a code? <a href="#resend">Resend</a></p> <a href="/auth/login">← Back to sign in</a> </footer> </form> </layout-card></body>
Progressive Enhancement
The OTP input pattern provides graceful degradation:
- Without JS: Standard text input accepting 6 digits
- With JS: Multi-box UI with automatic focus advance, backspace navigation, and paste support
No custom JavaScript required - the <form-field> enhancement handles everything automatically when it detects data-type="otp".
Form Field Features
All these patterns use <form-field> which provides:
- Validation icons: Checkmark/X appear after user interaction (disable with
data-no-icon) - Password toggle: Show/hide button added automatically to password fields
- Error messages: Use
<output class="error">witharia-live="polite" - Required indicator: Asterisk added automatically to required field labels
Usage Notes
- Autocomplete: Use
autocomplete="email"andautocomplete="current-password"for password manager support - OTP autocomplete: Use
autocomplete="one-time-code"for SMS/app code autofill - Remember me: Include a checkbox for persistent sessions
- Password recovery: Always provide a visible "Forgot password?" link
- Social login: Use
<icon-wc>with brand icons for recognizable buttons - Accessibility: All inputs have labels, validation messages use
aria-live, and focus states are visible
Related
Registration
Sign up forms with password requirements
Form Field
Form field element with validation
Text Divider
Horizontal divider with text
Brand Mark
Brand/logo display element