The form skeleton
<form>
<label for="email">Email</label>
<input id="email" type="email" required>
<label for="pwd">Password</label>
<input id="pwd" type="password" minlength="8" required>
<textarea rows="3"></textarea>
<button type="submit">Sign in</button>
</form>
Why label + for/id: Clicking the label focuses the input, meaning bigger hit area. Screen readers announce label text. Always pair them.
Input types: free validation and keyboards
| type | What you get |
|---|---|
email | Validates @, shows @ keyboard on mobile |
password | Dots, show/hide toggle (browser) |
number | Numeric keyboard, min/max/step |
tel | Phone keyboard |
url | Validates URL, .com key |
search | Clear button |
checkbox / radio | Toggles / single-choice |
select | Dropdown |
<input type="email" required> <!-- validates @ -->
<input type="number" min="1" max="10">
<input type="checkbox" required> <!-- must check -->
<select><option>Cat</option></select>
Gotcha: Don't use
type="text" for everything. Doing this means you lose mobile keyboards and free validation. Use the specific type.Pseudo-classes and pseudo-elements
- item 1
- item 2 with :nth-child(2)
- item 3
::before adds marker
| Selector | When |
|---|---|
:hover | Mouse over |
:focus | Keyboard/tab focused |
:active | Being pressed |
:checked | Checkbox/radio ticked |
:nth-child(n) | nth child |
::before / ::after | Generated content (needs content: "") |
:has() | Parent that has… (e.g., label:has(:checked)) |
input:hover { border-color: #0b57d0; }
input:focus { outline: 2px solid #0b57d0; outline-offset: 2px; }
input:checked { accent-color: #0b57d0; }
li:nth-child(2) { background: #fff7cc; }
.tag::before { content: "#"; color: #888; }
label:has(input:checked) { background: #eef6ff; }
Outline versus border: Do not remove
outline on :focus without replacing it. Keyboard users need it. Use outline (it does not affect layout) or style :focus-visible for keyboard only.Focus, cursor, and polish
button { cursor: pointer; }
button:disabled { cursor: not-allowed; opacity: 0.6; }
input:focus-visible { outline: 2px solid #0b57d0; outline-offset: 2px; }
Live demo: usable form, keyboard only
Tab through this form. All focus states are visible, labels are clickable, and validation is native.
- Build the form above with correct
label for/idandtype="email"andpassword. - Add
:hover,:focus-visible, and:activestyles. Make focus obvious. - Style
:checkedviaaccent-colororlabel:has(:checked). - Unplug mouse. Tab through, fill, and submit. Is every control reachable?