A login form looks simple and is a good test of everything in the forms chapter: labels, focus states, spacing and accessibility.
The finished form
<style>
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; font-family: system-ui, sans-serif; background: #f4ece6; min-height: 100vh; display: grid; place-items: center; padding: 16px; }
.login { width: 100%; max-width: 340px; background: #fff; padding: 28px; border-radius: 14px; box-shadow: 0 8px 24px rgba(0,0,0,.1); }
.login h1 { margin: 0 0 4px; font-size: 1.5rem; }
.login p.sub { margin: 0 0 20px; color: #5c5750; font-size: .9375rem; }
.field { margin-bottom: 16px; }
.field label { display: block; margin-bottom: 4px; font-weight: 600; font-size: .9375rem; }
.field input {
width: 100%; min-height: 44px; padding: 8px 12px; font: inherit;
border: 2px solid #b9b2ad; border-radius: 8px; background: #fff;
}
.field input:focus-visible { outline: 3px solid #1a527655; border-color: #1a5276; }
.field input:user-invalid { border-color: #c0392b; background: #fdf2f2; }
.field .error { display: none; margin-top: 4px; color: #b00020; font-size: .875rem; }
.field input:user-invalid ~ .error { display: block; }
.row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 18px; font-size: .875rem; }
.row a { color: #1a5276; }
.row label { display: flex; align-items: center; gap: 6px; }
.row input { accent-color: #d94a00; width: 18px; height: 18px; }
.submit { width: 100%; min-height: 46px; border: 0; border-radius: 8px; background: #d94a00; color: #fff; font: inherit; font-weight: 700; cursor: pointer; transition: background .15s; }
.submit:hover { background: #b84624; }
.submit:focus-visible { outline: 3px solid #1a5276; outline-offset: 2px; }
</style>
<form class="login">
<h1>Welcome back</h1>
<p class="sub">Sign in to see your orders.</p>
<div class="field">
<label for="email">Email</label>
<input id="email" type="email" autocomplete="email" required>
<span class="error">Enter a valid email address.</span>
</div>
<div class="field">
<label for="pw">Password</label>
<input id="pw" type="password" autocomplete="current-password" minlength="8" required>
<span class="error">Use at least 8 characters.</span>
</div>
<div class="row">
<label><input type="checkbox"> Remember me</label>
<a href="#">Forgot password?</a>
</div>
<button class="submit" type="submit">Sign in</button>
</form>
Where each idea comes from
display: grid; place-items: centeronbodycenters the form on the screen.max-widthwithwidth: 100%makes the form fluid on small screens.- Labels sit above fields, with a 44px minimum field height for touch.
:user-invalid ~ .errorshows the error message only after the visitor has interacted, using the general sibling selector.- Focus uses
:focus-visiblewith a clear ring. - The button is a real
<button>, full width, with hover and focus styles.
Check yourself
- Tab through the whole form. Can you always tell where you are?
- Zoom the page to 200%. Does anything overlap?
- Type “abc” in the email field and click out. Does the error show?