Build beautiful, accessible, and user-friendly forms β from basic inputs to custom checkboxes, styled selects, and complete registration forms with validation states.
Background
Forms are how users interact with your application. A poorly designed form loses customers; a well-designed one converts them. This lab covers all HTML form elements, CSS styling techniques, and validation states.
π‘ Use semantic input types (email, tel, number, date) β browsers add automatic validation, mobile keyboards show the appropriate layout (number pad for tel, email keyboard for email), and accessibility tools announce them correctly.
πΈ Verified Output:
Step 2: Form Validation Attributes
Write this file:
π‘ HTML5 validation attributes work without JavaScript: required (must be filled), minlength/maxlength (character limits), min/max (number/date range), pattern (regex match). The :valid and :invalid pseudo-classes let you style states with CSS.
πΈ Verified Output:
Step 3: Form Layout with CSS Grid
Write this file:
π‘ CSS Grid for forms is perfect β grid-template-columns: 1fr 1fr creates a two-column layout. grid-column: 1 / -1 makes any field span the full width. The form collapses to single-column on mobile with one @media query.
πΈ Verified Output:
Step 4: Custom Checkbox & Radio Styling
Write this file:
π‘ The CSS-only custom control pattern: Hide the native input (opacity: 0), place a sibling <span> right after it, then use input:checked + span to style the checked state. The label wrapping both elements makes the whole area clickable. No JavaScript needed.
πΈ Verified Output:
Step 5: Input Focus & Validation States
Write this file:
π‘ CSS pseudo-classes for validation::valid and :invalid check HTML5 validation rules; :placeholder-shown detects empty fields (so you don't show red on empty required fields before the user touches them). :focus for keyboard/click focus styling. :disabled for disabled inputs.
πΈ Verified Output:
Step 6: Custom Select Dropdown
Write this file:
π‘ appearance: none removes the browser's default select styling, letting you fully customize it. You lose the dropdown arrow, so add it back with a ::after pseudo-element. pointer-events: none on the arrow prevents it from blocking click events on the select.
πΈ Verified Output:
Step 7: File Upload Styling
Write this file:
π‘ Custom file inputs: Hide the real <input type="file"> and wrap everything in a <label> β clicking anywhere in the label triggers the file picker. The accept attribute filters allowed file types. This pattern works without JavaScript for basic uploads.
πΈ Verified Output:
Step 8: Capstone β Styled Registration Form
Write this file:
π‘ Registration form best practices: Two-column name row (feels shorter), clear labels above inputs (not inside as placeholder β accessibility), visible validation states, prominent CTA button with hover feedback, and alternative sign-in methods. The gradient background makes even a simple form feel premium.
docker run --rm -v /tmp:/workspace zchencow/innozverse-htmlcss:latest node -e "
const fs = require('fs');
const html = fs.readFileSync('/workspace/form-step1.html', 'utf8');
console.log(html.includes('<form>') ? 'β Form element found' : 'β Missing');
console.log(html.includes('type=\"email\"') ? 'β Email input type found' : 'β Missing email type');
"
β Form element found
β Email input type found