Table of Contents
- Introduction to Forms
- Form Structure and Elements
- Input Types
- Form Validation
- Styling Forms with CSS
- Advanced Form Components
- Form Layouts
- Form Validation Techniques
- Custom Form Elements
- Accessibility in Forms
- Form Security
- Practical Examples
- Best Practices
Introduction to Forms
Forms are the primary way users interact with websites - they're used for login, registration, search, feedback, purchases, and much more. A well-designed form can significantly improve user experience and conversion rates.
Why Forms Matter
- User Interaction: Primary method for user input
- Data Collection: Gather information from users
- Authentication: Login and registration
- E-commerce: Checkout and payment processing
- Communication: Contact forms and feedback
- Search: Query submission
Form Structure and Elements
Basic Form Structure
<!-- Basic form structure --> <form action="/submit-form" method="post" enctype="multipart/form-data"> <!-- Form fields go here --> <button type="submit">Submit</button> </form> <!-- Form attributes explained --> <form action="/api/users" <!-- Where to send the data --> method="post" <!-- HTTP method: get, post --> enctype="multipart/form-data" <!-- Encoding for file uploads --> name="registration" <!-- Form name --> id="register-form" <!-- Form ID --> class="form" <!-- CSS class --> autocomplete="on" <!-- Enable/disable autocomplete --> novalidate <!-- Disable browser validation --> target="_blank" <!-- Where to display response --> >
Fieldset and Legend
<!-- Grouping related fields --> <fieldset> <legend>Personal Information</legend> <div class="form-group"> <label for="first-name">First Name:</label> <input type="text" id="first-name" name="first_name"> </div> <div class="form-group"> <label for="last-name">Last Name:</label> <input type="text" id="last-name" name="last_name"> </div> </fieldset> <fieldset> <legend>Account Information</legend> <div class="form-group"> <label for="username">Username:</label> <input type="text" id="username" name="username"> </div> <div class="form-group"> <label for="password">Password:</label> <input type="password" id="password" name="password"> </div> </fieldset>
Labels and Input Association
<!-- Method 1: Using for and id attributes --> <label for="email">Email Address:</label> <input type="email" id="email" name="email"> <!-- Method 2: Wrapping input with label --> <label> Email Address: <input type="email" name="email"> </label> <!-- Method 3: Multiple labels (avoid) --> <label for="phone">Phone:</label> <label for="phone">(optional)</label> <input type="tel" id="phone" name="phone">
Input Types
Text Inputs
<!-- Basic text input --> <input type="text" name="username" placeholder="Enter username"> <!-- Password input --> <input type="password" name="password" placeholder="********"> <!-- Email input with validation --> <input type="email" name="email" placeholder="[email protected]" required> <!-- Telephone input --> <input type="tel" name="phone" placeholder="123-456-7890" pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}"> <!-- URL input --> <input type="url" name="website" placeholder="https://example.com"> <!-- Search input --> <input type="search" name="q" placeholder="Search..."> <!-- Textarea (multi-line) --> <textarea name="message" rows="5" cols="40" placeholder="Enter your message"></textarea>
Number Inputs
<!-- Basic number input --> <input type="number" name="age" min="0" max="150" step="1" value="18"> <!-- Range slider --> <input type="range" name="volume" min="0" max="100" value="50" step="1"> <!-- With datalist (suggestions) --> <input type="text" name="country" list="countries"> <datalist id="countries"> <option value="United States"> <option value="Canada"> <option value="Mexico"> <option value="United Kingdom"> </datalist>
Date and Time Inputs
<!-- Date picker --> <input type="date" name="birthday" min="1900-01-01" max="2024-12-31"> <!-- Time picker --> <input type="time" name="appointment" min="09:00" max="17:00"> <!-- DateTime local --> <input type="datetime-local" name="meeting"> <!-- Month picker --> <input type="month" name="billing-month"> <!-- Week picker --> <input type="week" name="week-number">
Selection Inputs
<!-- Checkbox --> <label> <input type="checkbox" name="subscribe" value="newsletter" checked> Subscribe to newsletter </label> <!-- Multiple checkboxes --> <fieldset> <legend>Interests</legend> <label> <input type="checkbox" name="interests[]" value="tech"> Technology </label> <label> <input type="checkbox" name="interests[]" value="sports"> Sports </label> <label> <input type="checkbox" name="interests[]" value="music"> Music </label> </fieldset> <!-- Radio buttons (mutually exclusive) --> <fieldset> <legend>Gender</legend> <label> <input type="radio" name="gender" value="male" checked> Male </label> <label> <input type="radio" name="gender" value="female"> Female </label> <label> <input type="radio" name="gender" value="other"> Other </label> </fieldset> <!-- Select dropdown --> <label for="country">Country:</label> <select id="country" name="country" required> <option value="">-- Select your country --</option> <option value="us">United States</option> <option value="ca">Canada</option> <option value="uk">United Kingdom</option> <option value="au">Australia</option> </select> <!-- Select with optgroups --> <label for="car">Car model:</label> <select id="car" name="car"> <optgroup label="American"> <option value="ford">Ford</option> <option value="chevrolet">Chevrolet</option> <option value="tesla">Tesla</option> </optgroup> <optgroup label="European"> <option value="bmw">BMW</option> <option value="audi">Audi</option> <option value="mercedes">Mercedes</option> </optgroup> <optgroup label="Japanese"> <option value="toyota">Toyota</option> <option value="honda">Honda</option> <option value="nissan">Nissan</option> </optgroup> </select> <!-- Multiple select --> <label for="colors">Favorite colors (hold Ctrl to select multiple):</label> <select id="colors" name="colors[]" multiple size="4"> <option value="red">Red</option> <option value="green">Green</option> <option value="blue">Blue</option> <option value="yellow">Yellow</option> <option value="purple">Purple</option> <option value="orange">Orange</option> </select>
File Inputs
<!-- Basic file upload --> <input type="file" name="document"> <!-- File upload with accept attribute --> <input type="file" name="profile-pic" accept="image/*"> <input type="file" name="resume" accept=".pdf,.doc,.docx"> <input type="file" name="photo" accept="image/png, image/jpeg"> <!-- Multiple file upload --> <input type="file" name="gallery" multiple accept="image/*"> <!-- File upload with capture (mobile) --> <input type="file" name="selfie" accept="image/*" capture="user"> <input type="file" name="document" accept="image/*" capture="environment">
Hidden and Button Inputs
<!-- Hidden input (for CSRF tokens, IDs, etc.) --> <input type="hidden" name="user_id" value="12345"> <input type="hidden" name="csrf_token" value="abc123def456"> <!-- Submit button --> <input type="submit" value="Save Changes"> <!-- Reset button (use carefully!) --> <input type="reset" value="Clear Form"> <!-- Generic button --> <input type="button" value="Click Me" onclick="alert('Clicked!')"> <!-- Image as button --> <input type="image" src="submit-button.png" alt="Submit" width="100"> HTML5 Input Attributes
<!-- Placeholder text --> <input type="text" placeholder="Enter your name"> <!-- Required field --> <input type="email" required> <!-- Disabled field --> <input type="text" value="Cannot edit" disabled> <!-- Readonly field --> <input type="text" value="Can see but not edit" readonly> <!-- Autofocus on page load --> <input type="text" autofocus> <!-- Autocomplete --> <input type="text" name="full-name" autocomplete="name"> <input type="email" name="email" autocomplete="email"> <input type="tel" name="phone" autocomplete="tel"> <!-- Pattern validation --> <input type="text" pattern="[A-Za-z]{3,}" title="At least 3 letters"> <!-- Min/max length --> <input type="text" minlength="3" maxlength="20"> <!-- Step attribute --> <input type="number" step="5" min="0" max="100"> <!-- Multiple values (email) --> <input type="email" multiple placeholder="Enter emails separated by commas"> <!-- Spellcheck --> <textarea spellcheck="true"></textarea> <!-- Wrap attribute for textarea --> <textarea wrap="soft"></textarea> <!-- Default --> <textarea wrap="hard"></textarea> <!-- Preserves line breaks --> <!-- Input mode for mobile keyboards --> <input type="text" inputmode="numeric"> <!-- Shows numeric keyboard --> <input type="text" inputmode="email"> <!-- Shows email keyboard --> <input type="text" inputmode="tel"> <!-- Shows telephone keyboard --> <input type="text" inputmode="url"> <!-- Shows URL keyboard --> <input type="text" inputmode="search"> <!-- Shows search keyboard --> Form Validation
HTML5 Built-in Validation
<!-- Required fields --> <input type="text" required> <!-- Email validation --> <input type="email" required> <!-- URL validation --> <input type="url" required> <!-- Number range --> <input type="number" min="18" max="99" required> <!-- Pattern validation --> <input type="text" pattern="[A-Za-z0-9]{5,10}" title="5-10 alphanumeric characters"> <!-- Length validation --> <input type="text" minlength="3" maxlength="20" required> <!-- Step validation --> <input type="number" step="5" min="0" max="100"> <!-- Custom validation with JavaScript --> <script> function validateForm() { const password = document.getElementById('password').value; const confirm = document.getElementById('confirm-password').value; if (password !== confirm) { alert('Passwords do not match!'); return false; } return true; } </script> <form onsubmit="return validateForm()"> <input type="password" id="password" required> <input type="password" id="confirm-password" required> <button type="submit">Register</button> </form> Constraint Validation API
<form id="registration-form"> <div class="form-group"> <label for="username">Username:</label> <input type="text" id="username" name="username" required minlength="3" maxlength="20" pattern="[A-Za-z0-9_]+" title="Only letters, numbers, and underscores"> <span class="error-message" id="username-error"></span> </div> <div class="form-group"> <label for="email">Email:</label> <input type="email" id="email" name="email" required> <span class="error-message" id="email-error"></span> </div> <div class="form-group"> <label for="age">Age:</label> <input type="number" id="age" name="age" min="18" max="120" required> <span class="error-message" id="age-error"></span> </div> <button type="submit">Register</button> </form> <script> document.getElementById('registration-form').addEventListener('submit', function(e) { e.preventDefault(); const username = document.getElementById('username'); const email = document.getElementById('email'); const age = document.getElementById('age'); let isValid = true; // Username validation if (!username.validity.valid) { document.getElementById('username-error').textContent = username.validationMessage || 'Username must be 3-20 alphanumeric characters'; isValid = false; } else { document.getElementById('username-error').textContent = ''; } // Email validation if (!email.validity.valid) { document.getElementById('email-error').textContent = email.validationMessage || 'Please enter a valid email'; isValid = false; } else { document.getElementById('email-error').textContent = ''; } // Age validation if (!age.validity.valid) { document.getElementById('age-error').textContent = age.validationMessage || 'You must be at least 18 years old'; isValid = false; } else { document.getElementById('age-error').textContent = ''; } if (isValid) { // Submit form this.submit(); } }); // Real-time validation document.getElementById('username').addEventListener('input', function() { if (this.validity.valid) { this.classList.remove('invalid'); this.classList.add('valid'); document.getElementById('username-error').textContent = ''; } else { this.classList.remove('valid'); this.classList.add('invalid'); } }); </script> Custom Validation Messages
<form id="custom-validation-form"> <div class="form-group"> <label for="password">Password:</label> <input type="password" id="password" name="password" required minlength="8" pattern="^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$" title="At least 8 characters, one letter and one number"> <span class="error-message" id="password-error"></span> </div> <div class="form-group"> <label for="confirm-password">Confirm Password:</label> <input type="password" id="confirm-password" name="confirm-password" required> <span class="error-message" id="confirm-error"></span> </div> <button type="submit">Change Password</button> </form> <script> document.getElementById('custom-validation-form').addEventListener('submit', function(e) { e.preventDefault(); const password = document.getElementById('password'); const confirm = document.getElementById('confirm-password'); // Check password strength const hasLetter = /[A-Za-z]/.test(password.value); const hasNumber = /\d/.test(password.value); const hasMinLength = password.value.length >= 8; if (!hasLetter || !hasNumber || !hasMinLength) { password.setCustomValidity('Password must be at least 8 characters with at least one letter and one number'); } else { password.setCustomValidity(''); } // Check password match if (password.value !== confirm.value) { confirm.setCustomValidity('Passwords do not match'); } else { confirm.setCustomValidity(''); } if (password.checkValidity() && confirm.checkValidity()) { alert('Form is valid!'); // this.submit(); } else { password.reportValidity(); confirm.reportValidity(); } }); </script> Styling Forms with CSS
Basic Form Styling
/* Form container */ .form-container { max-width: 500px; margin: 2rem auto; padding: 2rem; background: #f9f9f9; border-radius: 10px; box-shadow: 0 5px 20px rgba(0,0,0,0.1); } /* Form groups */ .form-group { margin-bottom: 1.5rem; } /* Labels */ label { display: block; margin-bottom: 0.5rem; font-weight: 600; color: #333; font-size: 0.9rem; text-transform: uppercase; letter-spacing: 0.5px; } /* Required field indicator */ label.required::after { content: ' *'; color: #ff4444; font-weight: normal; } /* Input styling */ input, select, textarea { width: 100%; padding: 0.75rem 1rem; border: 2px solid #e0e0e0; border-radius: 6px; font-size: 1rem; font-family: inherit; transition: all 0.3s ease; background: white; } /* Focus state */ input:focus, select:focus, textarea:focus { outline: none; border-color: #4CAF50; box-shadow: 0 0 0 3px rgba(76, 175, 80, 0.1); } /* Hover state */ input:hover, select:hover, textarea:hover { border-color: #b0b0b0; } /* Disabled state */ input:disabled, select:disabled, textarea:disabled { background: #f5f5f5; cursor: not-allowed; opacity: 0.7; } /* Readonly state */ input:readonly, textarea:readonly { background: #f9f9f9; border-color: #ddd; cursor: default; } /* Placeholder styling */ ::placeholder { color: #999; opacity: 1; } /* Validation states */ input.valid, select.valid, textarea.valid { border-color: #4CAF50; } input.invalid, select.invalid, textarea.invalid { border-color: #ff4444; } /* Error messages */ .error-message { color: #ff4444; font-size: 0.85rem; margin-top: 0.25rem; display: block; } /* Success messages */ .success-message { color: #4CAF50; font-size: 0.85rem; margin-top: 0.25rem; display: block; } Button Styling
/* Base button styles */ button, .button { display: inline-block; padding: 0.75rem 1.5rem; border: none; border-radius: 6px; font-size: 1rem; font-weight: 600; text-decoration: none; cursor: pointer; transition: all 0.3s ease; } /* Primary button */ .btn-primary { background: #4CAF50; color: white; } .btn-primary:hover { background: #45a049; transform: translateY(-2px); box-shadow: 0 5px 15px rgba(76, 175, 80, 0.3); } .btn-primary:active { transform: translateY(0); } /* Secondary button */ .btn-secondary { background: #f0f0f0; color: #333; } .btn-secondary:hover { background: #e0e0e0; transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0,0,0,0.1); } /* Danger button */ .btn-danger { background: #ff4444; color: white; } .btn-danger:hover { background: #ff3333; transform: translateY(-2px); box-shadow: 0 5px 15px rgba(255, 68, 68, 0.3); } /* Outline button */ .btn-outline { background: transparent; border: 2px solid #4CAF50; color: #4CAF50; } .btn-outline:hover { background: #4CAF50; color: white; } /* Disabled button */ button:disabled, .button:disabled { opacity: 0.5; cursor: not-allowed; pointer-events: none; } /* Full width button */ .btn-block { display: block; width: 100%; } /* Button sizes */ .btn-small { padding: 0.4rem 0.8rem; font-size: 0.875rem; } .btn-large { padding: 1rem 2rem; font-size: 1.125rem; } /* Button group */ .button-group { display: flex; gap: 10px; } .button-group .btn { flex: 1; } Custom Select Styling
/* Custom select wrapper */ .custom-select { position: relative; width: 100%; } /* Hide default arrow */ .custom-select select { appearance: none; -webkit-appearance: none; width: 100%; padding: 0.75rem 1rem; background: white; border: 2px solid #e0e0e0; border-radius: 6px; cursor: pointer; } /* Custom arrow */ .custom-select::after { content: '▼'; position: absolute; top: 50%; right: 1rem; transform: translateY(-50%); pointer-events: none; color: #666; font-size: 0.8rem; } /* Multiple select */ select[multiple] { padding: 0; height: auto; } select[multiple] option { padding: 0.5rem 1rem; } select[multiple] option:checked { background: #4CAF50 linear-gradient(0deg, #4CAF50 0%, #4CAF50 100%); color: white; } Checkbox and Radio Styling
/* Hide default checkbox */ .custom-checkbox { position: relative; display: inline-block; padding-left: 30px; margin-right: 15px; cursor: pointer; user-select: none; } .custom-checkbox input { position: absolute; opacity: 0; cursor: pointer; height: 0; width: 0; } /* Custom checkbox */ .checkmark { position: absolute; top: 0; left: 0; height: 20px; width: 20px; background-color: #fff; border: 2px solid #e0e0e0; border-radius: 4px; transition: all 0.3s; } /* Hover state */ .custom-checkbox:hover input ~ .checkmark { border-color: #4CAF50; } /* Checked state */ .custom-checkbox input:checked ~ .checkmark { background-color: #4CAF50; border-color: #4CAF50; } /* Checkmark icon */ .checkmark:after { content: ""; position: absolute; display: none; } .custom-checkbox input:checked ~ .checkmark:after { display: block; } .custom-checkbox .checkmark:after { left: 6px; top: 2px; width: 5px; height: 10px; border: solid white; border-width: 0 2px 2px 0; transform: rotate(45deg); } /* Focus state */ .custom-checkbox input:focus ~ .checkmark { box-shadow: 0 0 0 3px rgba(76, 175, 80, 0.3); } /* Disabled state */ .custom-checkbox input:disabled ~ .checkmark { opacity: 0.5; cursor: not-allowed; } /* Radio button styling */ .custom-radio { position: relative; display: inline-block; padding-left: 30px; margin-right: 15px; cursor: pointer; user-select: none; } .custom-radio input { position: absolute; opacity: 0; } .radio-mark { position: absolute; top: 0; left: 0; height: 20px; width: 20px; background-color: #fff; border: 2px solid #e0e0e0; border-radius: 50%; transition: all 0.3s; } .custom-radio:hover input ~ .radio-mark { border-color: #4CAF50; } .custom-radio input:checked ~ .radio-mark { border-color: #4CAF50; background-color: #fff; } .radio-mark:after { content: ""; position: absolute; display: none; top: 4px; left: 4px; width: 8px; height: 8px; border-radius: 50%; background: #4CAF50; } .custom-radio input:checked ~ .radio-mark:after { display: block; } /* Inline checkboxes/radios */ .checkbox-group, .radio-group { display: flex; flex-wrap: wrap; gap: 15px; align-items: center; } Toggle Switch (Checkbox)
/* Toggle switch */ .toggle-switch { position: relative; display: inline-block; width: 60px; height: 34px; } .toggle-switch input { opacity: 0; width: 0; height: 0; } .toggle-slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; transition: .4s; border-radius: 34px; } .toggle-slider:before { position: absolute; content: ""; height: 26px; width: 26px; left: 4px; bottom: 4px; background-color: white; transition: .4s; border-radius: 50%; } input:checked + .toggle-slider { background-color: #4CAF50; } input:focus + .toggle-slider { box-shadow: 0 0 1px #4CAF50; } input:checked + .toggle-slider:before { transform: translateX(26px); } /* Disabled state */ input:disabled + .toggle-slider { opacity: 0.5; cursor: not-allowed; } File Input Styling
/* File input styling */ .file-input-wrapper { position: relative; display: inline-block; width: 100%; } .file-input-wrapper input[type="file"] { position: absolute; top: 0; left: 0; opacity: 0; width: 100%; height: 100%; cursor: pointer; z-index: 2; } .file-input-button { display: flex; align-items: center; justify-content: center; width: 100%; padding: 1rem; background: #f0f0f0; border: 2px dashed #ccc; border-radius: 6px; color: #666; font-size: 1rem; transition: all 0.3s; } .file-input-wrapper:hover .file-input-button { border-color: #4CAF50; color: #4CAF50; background: #f5f5f5; } .file-name { margin-top: 0.5rem; font-size: 0.875rem; color: #666; } /* Multiple file upload preview */ .file-preview { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px; } .file-preview-item { position: relative; width: 80px; height: 80px; border: 1px solid #ddd; border-radius: 4px; overflow: hidden; } .file-preview-item img { width: 100%; height: 100%; object-fit: cover; } .file-preview-item .remove-file { position: absolute; top: 2px; right: 2px; width: 20px; height: 20px; background: rgba(255, 68, 68, 0.8); color: white; border: none; border-radius: 50%; cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 12px; } Form Validation Styling
/* Validation styles */ input:valid { border-color: #4CAF50; } input:invalid:not(:focus):not(:placeholder-shown) { border-color: #ff4444; } /* Validation icons */ .input-wrapper { position: relative; } .input-wrapper::after { position: absolute; top: 50%; right: 10px; transform: translateY(-50%); font-size: 1rem; } .input-wrapper input:valid + .validation-icon::before { content: '✓'; color: #4CAF50; } .input-wrapper input:invalid:not(:focus):not(:placeholder-shown) + .validation-icon::before { content: '✗'; color: #ff4444; } /* Progress bar for password strength */ .password-strength { height: 4px; margin-top: 5px; background: #e0e0e0; border-radius: 2px; overflow: hidden; } .password-strength-bar { height: 100%; width: 0; transition: width 0.3s, background 0.3s; } .password-strength-bar.weak { width: 33.33%; background: #ff4444; } .password-strength-bar.medium { width: 66.66%; background: #ffbb33; } .password-strength-bar.strong { width: 100%; background: #4CAF50; } /* Password requirements list */ .password-requirements { list-style: none; padding: 0; margin: 10px 0 0; font-size: 0.85rem; } .password-requirements li { margin-bottom: 5px; color: #999; } .password-requirements li.valid { color: #4CAF50; } .password-requirements li.valid::before { content: '✓ '; } .password-requirements li.invalid::before { content: '✗ '; } Advanced Form Components
Autocomplete and Datalist
<!-- Simple datalist --> <label for="browser">Choose your browser:</label> <input list="browsers" id="browser" name="browser"> <datalist id="browsers"> <option value="Chrome"> <option value="Firefox"> <option value="Safari"> <option value="Edge"> <option value="Opera"> </datalist> <!-- Datalist with labels --> <label for="country">Country:</label> <input list="countries" id="country" name="country"> <datalist id="countries"> <option value="US" label="United States"> <option value="CA" label="Canada"> <option value="UK" label="United Kingdom"> <option value="AU" label="Australia"> </datalist>
Input Groups
<!-- Input groups with addons --> <div class="input-group"> <span class="input-group-text">@</span> <input type="text" placeholder="Username"> </div> <div class="input-group"> <span class="input-group-text">https://</span> <input type="url" placeholder="example.com"> <span class="input-group-text">.com</span> </div> <div class="input-group"> <span class="input-group-text">$</span> <input type="number" placeholder="Amount" min="0" step="0.01"> <span class="input-group-text">.00</span> </div> <div class="input-group"> <input type="text" placeholder="Search"> <button class="input-group-button">🔍</button> </div>
/* Input group styling */ .input-group { display: flex; align-items: stretch; width: 100%; } .input-group-text { display: flex; align-items: center; padding: 0.75rem 1rem; background: #f0f0f0; border: 2px solid #e0e0e0; color: #666; font-size: 1rem; } .input-group-text:first-child { border-right: none; border-radius: 6px 0 0 6px; } .input-group-text:last-child { border-left: none; border-radius: 0 6px 6px 0; } .input-group input { flex: 1; min-width: 0; border-radius: 0; } .input-group input:first-child { border-radius: 6px 0 0 6px; } .input-group input:last-child { border-radius: 0 6px 6px 0; } .input-group-button { padding: 0 1rem; background: #4CAF50; color: white; border: 2px solid #4CAF50; border-radius: 0 6px 6px 0; cursor: pointer; transition: all 0.3s; } .input-group-button:hover { background: #45a049; } Floating Labels
<div class="floating-label-group"> <input type="text" id="floating-name" placeholder=" "> <label for="floating-name">Full Name</label> </div> <div class="floating-label-group"> <input type="email" id="floating-email" placeholder=" "> <label for="floating-email">Email Address</label> </div> <div class="floating-label-group"> <textarea id="floating-message" placeholder=" "></textarea> <label for="floating-message">Message</label> </div>
/* Floating labels */ .floating-label-group { position: relative; margin-bottom: 1.5rem; } .floating-label-group input, .floating-label-group textarea { width: 100%; padding: 1rem 0.75rem 0.5rem; border: 2px solid #e0e0e0; border-radius: 6px; font-size: 1rem; transition: all 0.3s; } .floating-label-group label { position: absolute; top: 0.75rem; left: 0.75rem; padding: 0 0.25rem; background: white; color: #999; font-size: 1rem; transition: all 0.3s; pointer-events: none; } .floating-label-group input:focus, .floating-label-group textarea:focus, .floating-label-group input:not(:placeholder-shown), .floating-label-group textarea:not(:placeholder-shown) { padding-top: 1.25rem; padding-bottom: 0.25rem; } .floating-label-group input:focus + label, .floating-label-group textarea:focus + label, .floating-label-group input:not(:placeholder-shown) + label, .floating-label-group textarea:not(:placeholder-shown) + label { top: -0.5rem; left: 0.5rem; font-size: 0.8rem; color: #4CAF50; } .floating-label-group input:focus, .floating-label-group textarea:focus { border-color: #4CAF50; } Character Counter
<div class="character-counter-group"> <label for="bio">Bio (max 200 characters):</label> <textarea id="bio" name="bio" maxlength="200" rows="4"></textarea> <div class="character-counter"> <span id="char-count">0</span>/200 characters </div> </div> <script> document.getElementById('bio').addEventListener('input', function() { const count = this.value.length; const counter = document.getElementById('char-count'); counter.textContent = count; if (count > 180) { counter.style.color = '#ff4444'; } else if (count > 150) { counter.style.color = '#ffbb33'; } else { counter.style.color = '#666'; } }); </script> Password Strength Meter
<div class="password-group"> <label for="password">Password:</label> <input type="password" id="password" name="password"> <div class="password-strength"> <div class="password-strength-bar" id="strength-bar"></div> </div> <ul class="password-requirements" id="password-requirements"> <li id="req-length">At least 8 characters</li> <li id="req-lower">At least one lowercase letter</li> <li id="req-upper">At least one uppercase letter</li> <li id="req-number">At least one number</li> <li id="req-special">At least one special character</li> </ul> </div> <script> document.getElementById('password').addEventListener('input', function() { const password = this.value; const bar = document.getElementById('strength-bar'); const reqs = { length: password.length >= 8, lower: /[a-z]/.test(password), upper: /[A-Z]/.test(password), number: /\d/.test(password), special: /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(password) }; // Update requirement indicators for (const [req, met] of Object.entries(reqs)) { const element = document.getElementById(`req-${req}`); if (met) { element.classList.add('valid'); element.classList.remove('invalid'); } else { element.classList.add('invalid'); element.classList.remove('valid'); } } // Calculate strength const metCount = Object.values(reqs).filter(Boolean).length; let strength = 0; let color = ''; if (metCount <= 2) { strength = 33.33; color = '#ff4444'; bar.className = 'password-strength-bar weak'; } else if (metCount <= 4) { strength = 66.66; color = '#ffbb33'; bar.className = 'password-strength-bar medium'; } else { strength = 100; color = '#4CAF50'; bar.className = 'password-strength-bar strong'; } bar.style.width = strength + '%'; bar.style.background = color; }); </script> Form Layouts
Single Column Layout
<div class="form-container"> <h2>Contact Us</h2> <form class="form-single-column"> <div class="form-group"> <label for="name">Full Name *</label> <input type="text" id="name" name="name" required> </div> <div class="form-group"> <label for="email">Email *</label> <input type="email" id="email" name="email" required> </div> <div class="form-group"> <label for="subject">Subject</label> <input type="text" id="subject" name="subject"> </div> <div class="form-group"> <label for="message">Message *</label> <textarea id="message" name="message" rows="5" required></textarea> </div> <button type="submit" class="btn btn-primary btn-block">Send Message</button> </form> </div>
Two Column Layout
<form class="form-two-column"> <div class="form-row"> <div class="form-group"> <label for="first-name">First Name *</label> <input type="text" id="first-name" name="first_name" required> </div> <div class="form-group"> <label for="last-name">Last Name *</label> <input type="text" id="last-name" name="last_name" required> </div> </div> <div class="form-row"> <div class="form-group"> <label for="email">Email *</label> <input type="email" id="email" name="email" required> </div> <div class="form-group"> <label for="phone">Phone</label> <input type="tel" id="phone" name="phone"> </div> </div> <div class="form-row"> <div class="form-group full-width"> <label for="address">Street Address</label> <input type="text" id="address" name="address"> </div> </div> <div class="form-row"> <div class="form-group"> <label for="city">City</label> <input type="text" id="city" name="city"> </div> <div class="form-group"> <label for="state">State</label> <select id="state" name="state"> <option value="">Select state</option> <option value="CA">California</option> <option value="NY">New York</option> <option value="TX">Texas</option> </select> </div> <div class="form-group"> <label for="zip">ZIP Code</label> <input type="text" id="zip" name="zip" pattern="[0-9]{5}"> </div> </div> <div class="form-actions"> <button type="submit" class="btn btn-primary">Submit</button> <button type="reset" class="btn btn-secondary">Reset</button> </div> </form> /* Two column form layout */ .form-two-column { max-width: 800px; margin: 0 auto; } .form-row { display: flex; gap: 20px; margin-bottom: 20px; } .form-row .form-group { flex: 1; margin-bottom: 0; } .form-group.full-width { flex: 0 0 100%; } .form-actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 30px; } /* Responsive */ @media (max-width: 768px) { .form-row { flex-direction: column; gap: 0; } .form-row .form-group { margin-bottom: 20px; } .form-actions { flex-direction: column; } .form-actions .btn { width: 100%; } } Multi-step Form (Wizard)
<div class="multi-step-form"> <!-- Progress indicator --> <div class="form-progress"> <div class="progress-step active" data-step="1"> <span class="step-number">1</span> <span class="step-label">Personal Info</span> </div> <div class="progress-step" data-step="2"> <span class="step-number">2</span> <span class="step-label">Account Details</span> </div> <div class="progress-step" data-step="3"> <span class="step-number">3</span> <span class="step-label">Confirmation</span> </div> </div> <!-- Form steps --> <form id="multi-step-form"> <!-- Step 1 --> <div class="form-step active" id="step-1"> <h3>Personal Information</h3> <div class="form-group"> <label for="full-name">Full Name *</label> <input type="text" id="full-name" name="full_name" required> </div> <div class="form-group"> <label for="email">Email *</label> <input type="email" id="email" name="email" required> </div> <div class="form-group"> <label for="phone">Phone Number</label> <input type="tel" id="phone" name="phone"> </div> <div class="form-navigation"> <button type="button" class="btn btn-primary next-step">Next</button> </div> </div> <!-- Step 2 --> <div class="form-step" id="step-2"> <h3>Account Details</h3> <div class="form-group"> <label for="username">Username *</label> <input type="text" id="username" name="username" required> </div> <div class="form-group"> <label for="password">Password *</label> <input type="password" id="password" name="password" required> </div> <div class="form-group"> <label for="confirm-password">Confirm Password *</label> <input type="password" id="confirm-password" name="confirm_password" required> </div> <div class="form-navigation"> <button type="button" class="btn btn-secondary prev-step">Previous</button> <button type="button" class="btn btn-primary next-step">Next</button> </div> </div> <!-- Step 3 --> <div class="form-step" id="step-3"> <h3>Review Your Information</h3> <div class="review-section"> <h4>Personal Information</h4> <p id="review-name"></p> <p id="review-email"></p> <p id="review-phone"></p> </div> <div class="review-section"> <h4>Account Details</h4> <p id="review-username"></p> <p id="review-password">••••••••</p> </div> <div class="form-navigation"> <button type="button" class="btn btn-secondary prev-step">Previous</button> <button type="submit" class="btn btn-primary">Submit</button> </div> </div> </form> </div> <script> document.addEventListener('DOMContentLoaded', function() { const steps = document.querySelectorAll('.form-step'); const progressSteps = document.querySelectorAll('.progress-step'); let currentStep = 0; function showStep(stepIndex) { // Hide all steps steps.forEach(step => step.classList.remove('active')); progressSteps.forEach(step => step.classList.remove('active')); // Show current step steps[stepIndex].classList.add('active'); progressSteps[stepIndex].classList.add('active'); // Update review section on last step if (stepIndex === 2) { document.getElementById('review-name').textContent = document.getElementById('full-name').value; document.getElementById('review-email').textContent = document.getElementById('email').value; document.getElementById('review-phone').textContent = document.getElementById('phone').value || 'Not provided'; document.getElementById('review-username').textContent = document.getElementById('username').value; } } // Next button document.querySelectorAll('.next-step').forEach(btn => { btn.addEventListener('click', function() { if (currentStep < steps.length - 1) { // Validate current step const currentStepInputs = steps[currentStep].querySelectorAll('input[required]'); let isValid = true; currentStepInputs.forEach(input => { if (!input.checkValidity()) { input.reportValidity(); isValid = false; } }); if (isValid) { currentStep++; showStep(currentStep); } } }); }); // Previous button document.querySelectorAll('.prev-step').forEach(btn => { btn.addEventListener('click', function() { if (currentStep > 0) { currentStep--; showStep(currentStep); } }); }); // Form submit document.getElementById('multi-step-form').addEventListener('submit', function(e) { e.preventDefault(); alert('Form submitted successfully!'); }); }); </script> /* Multi-step form styling */ .multi-step-form { max-width: 600px; margin: 0 auto; padding: 30px; background: white; border-radius: 10px; box-shadow: 0 5px 20px rgba(0,0,0,0.1); } .form-progress { display: flex; justify-content: space-between; margin-bottom: 30px; position: relative; } .form-progress::before { content: ''; position: absolute; top: 20px; left: 0; right: 0; height: 2px; background: #e0e0e0; z-index: 1; } .progress-step { position: relative; z-index: 2; text-align: center; flex: 1; } .step-number { display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; background: #e0e0e0; color: #666; border-radius: 50%; margin: 0 auto 10px; font-weight: bold; transition: all 0.3s; } .step-label { font-size: 0.875rem; color: #666; } .progress-step.active .step-number { background: #4CAF50; color: white; } .progress-step.active .step-label { color: #4CAF50; font-weight: 600; } .progress-step.completed .step-number { background: #4CAF50; color: white; } .form-step { display: none; } .form-step.active { display: block; } .form-navigation { display: flex; justify-content: space-between; margin-top: 30px; } .review-section { background: #f9f9f9; padding: 20px; border-radius: 6px; margin-bottom: 20px; } .review-section h4 { margin-top: 0; margin-bottom: 15px; color: #333; } .review-section p { margin: 5px 0; color: #666; } Custom Form Elements
Custom Range Slider
<div class="range-slider"> <label for="price">Price Range: $<span id="price-value">50</span></label> <input type="range" id="price" name="price" min="0" max="100" value="50"> <div class="range-labels"> <span>$0</span> <span>$25</span> <span>$50</span> <span>$75</span> <span>$100</span> </div> </div> <script> document.getElementById('price').addEventListener('input', function() { document.getElementById('price-value').textContent = this.value; }); </script> /* Custom range slider */ .range-slider { margin: 20px 0; } .range-slider input[type=range] { -webkit-appearance: none; width: 100%; height: 8px; background: #e0e0e0; border-radius: 4px; outline: none; } .range-slider input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; width: 20px; height: 20px; background: #4CAF50; border-radius: 50%; cursor: pointer; transition: all 0.3s; box-shadow: 0 2px 5px rgba(0,0,0,0.2); } .range-slider input[type=range]::-webkit-slider-thumb:hover { transform: scale(1.2); } .range-slider input[type=range]::-moz-range-thumb { width: 20px; height: 20px; background: #4CAF50; border-radius: 50%; cursor: pointer; transition: all 0.3s; border: none; } .range-slider input[type=range]::-moz-range-progress { background: #4CAF50; height: 8px; border-radius: 4px; } .range-labels { display: flex; justify-content: space-between; margin-top: 10px; color: #666; font-size: 0.875rem; } Custom Rating Stars
<div class="rating"> <p>Rate your experience:</p> <div class="stars"> <input type="radio" id="star5" name="rating" value="5"> <label for="star5" title="5 stars">★</label> <input type="radio" id="star4" name="rating" value="4"> <label for="star4" title="4 stars">★</label> <input type="radio" id="star3" name="rating" value="3"> <label for="star3" title="3 stars">★</label> <input type="radio" id="star2" name="rating" value="2"> <label for="star2" title="2 stars">★</label> <input type="radio" id="star1" name="rating" value="1"> <label for="star1" title="1 star">★</label> </div> </div>
/* Custom star rating */ .rating { margin: 20px 0; } .stars { display: inline-flex; flex-direction: row-reverse; gap: 5px; } .stars input { display: none; } .stars label { font-size: 30px; color: #ddd; cursor: pointer; transition: color 0.2s; } .stars label:hover, .stars label:hover ~ label { color: #ffd700; } .stars input:checked ~ label { color: #ffd700; } Custom Color Picker
<div class="color-picker-group"> <label for="color">Choose color:</label> <div class="color-picker"> <input type="color" id="color" name="color" value="#4CAF50"> <span class="color-value">#4CAF50</span> </div> </div> <script> document.getElementById('color').addEventListener('input', function() { document.querySelector('.color-value').textContent = this.value; }); </script> /* Custom color picker */ .color-picker { display: flex; align-items: center; gap: 10px; } .color-picker input[type=color] { width: 50px; height: 50px; border: 2px solid #e0e0e0; border-radius: 6px; cursor: pointer; } .color-value { font-family: monospace; padding: 0.5rem 1rem; background: #f0f0f0; border-radius: 4px; font-size: 0.9rem; } Accessibility in Forms
ARIA Attributes
<!-- Required fields with ARIA --> <label for="username">Username <span aria-hidden="true">*</span></label> <input type="text" id="username" aria-required="true" aria-describedby="username-help username-error"> <div id="username-help" class="help-text"> Must be at least 3 characters, letters and numbers only </div> <div id="username-error" class="error-message" role="alert"></div> <!-- Fieldset for grouping --> <fieldset> <legend>Contact Preferences</legend> <div> <input type="checkbox" id="contact-email" name="contact_email"> <label for="contact-email">Email</label> </div> <div> <input type="checkbox" id="contact-phone" name="contact_phone"> <label for="contact-phone">Phone</label> </div> </fieldset> <!-- Error summary --> <div id="error-summary" class="error-summary" role="alert" tabindex="-1" aria-labelledby="error-heading"> <h3 id="error-heading">Please correct the following errors:</h3> <ul> <li><a href="#username">Username is required</a></li> <li><a href="#email">Email must be valid</a></li> </ul> </div> <!-- Progress indicator --> <div role="status" aria-live="polite"> Step 2 of 3: Account Details </div> <!-- Loading state --> <button aria-busy="true" aria-label="Submitting form..." disabled> <span class="spinner"></span> Submitting... </button>
Keyboard Navigation
<!-- Tab index for custom order --> <form> <div class="form-group"> <label for="name">Name</label> <input type="text" id="name" tabindex="1"> </div> <div class="form-group"> <label for="email">Email</label> <input type="email" id="email" tabindex="2"> </div> <div class="form-group"> <label for="phone">Phone</label> <input type="tel" id="phone" tabindex="3"> </div> <button type="submit" tabindex="4">Submit</button> </form> <!-- Skip link for long forms --> <a href="#main-form" class="skip-link">Skip to form</a> <!-- Focus trap for modals --> <div class="modal" role="dialog" aria-modal="true" aria-labelledby="modal-title"> <h2 id="modal-title">Login</h2> <div class="modal-content"> <!-- Form fields --> <button class="close-modal" aria-label="Close">×</button> </div> </div>
Screen Reader Announcements
<!-- Live regions for dynamic updates --> <div aria-live="polite" aria-atomic="true" class="sr-only"> <!-- Screen reader will announce changes --> </div> <!-- Password strength announcement --> <div role="status" aria-live="polite" id="password-strength-status"> Password strength: Weak </div> <!-- Form submission status --> <div role="status" aria-live="assertive" class="form-status"> <span class="success-message" aria-hidden="true">✓</span> <span>Form submitted successfully!</span> </div>
Form Security
CSRF Protection
<!-- CSRF token in forms --> <form method="post" action="/submit"> <input type="hidden" name="csrf_token" value="random-generated-token"> <!-- form fields --> </form>
Input Sanitization Example
<!-- Client-side sanitization --> <script> function sanitizeInput(input) { // Remove potentially dangerous characters return input.replace(/[<>]/g, ''); } document.getElementById('comment').addEventListener('input', function(e) { this.value = sanitizeInput(this.value); }); </script> Rate Limiting Indicator
<!-- Rate limit indicator --> <div class="rate-limit"> <div class="attempts-left">3 attempts remaining</div> <div class="cooldown-timer" style="display: none;"> Too many attempts. Try again in <span id="timer">30</span> seconds </div> </div>
Practical Examples
Example 1: Complete Registration Form
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Registration Form</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 20px; } .register-container { background: white; border-radius: 20px; box-shadow: 0 20px 60px rgba(0,0,0,0.3); width: 100%; max-width: 500px; padding: 40px; } .register-header { text-align: center; margin-bottom: 30px; } .register-header h1 { color: #333; font-size: 2rem; margin-bottom: 10px; } .register-header p { color: #666; } .form-group { margin-bottom: 20px; } label { display: block; margin-bottom: 8px; color: #333; font-weight: 500; font-size: 0.9rem; } label.required::after { content: ' *'; color: #ff4444; } .input-wrapper { position: relative; } .input-wrapper i { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: #999; } input, select { width: 100%; padding: 12px 12px 12px 40px; border: 2px solid #e0e0e0; border-radius: 8px; font-size: 1rem; transition: all 0.3s; } input:focus, select:focus { outline: none; border-color: #667eea; box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); } input::placeholder { color: #aaa; } .password-requirements { margin-top: 10px; padding: 15px; background: #f8f9fa; border-radius: 8px; font-size: 0.85rem; } .password-requirements p { margin-bottom: 10px; color: #666; font-weight: 500; } .password-requirements ul { list-style: none; } .password-requirements li { margin-bottom: 5px; color: #999; display: flex; align-items: center; gap: 5px; } .password-requirements li.valid { color: #28a745; } .password-requirements li.invalid { color: #dc3545; } .password-strength { height: 4px; background: #e0e0e0; border-radius: 2px; margin: 10px 0; overflow: hidden; } .strength-bar { height: 100%; width: 0; transition: all 0.3s; } .strength-bar.weak { width: 33.33%; background: #dc3545; } .strength-bar.medium { width: 66.66%; background: #ffc107; } .strength-bar.strong { width: 100%; background: #28a745; } .checkbox-group { display: flex; align-items: center; gap: 10px; margin: 20px 0; } .checkbox-group input[type="checkbox"] { width: auto; padding: 0; } .checkbox-group label { margin: 0; font-weight: normal; } .checkbox-group a { color: #667eea; text-decoration: none; } .checkbox-group a:hover { text-decoration: underline; } .btn { width: 100%; padding: 14px; background: #667eea; color: white; border: none; border-radius: 8px; font-size: 1rem; font-weight: 600; cursor: pointer; transition: all 0.3s; } .btn:hover { background: #5a67d8; transform: translateY(-2px); box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4); } .btn:active { transform: translateY(0); } .btn:disabled { opacity: 0.5; cursor: not-allowed; } .login-link { text-align: center; margin-top: 20px; color: #666; } .login-link a { color: #667eea; text-decoration: none; font-weight: 500; } .login-link a:hover { text-decoration: underline; } .error-message { color: #dc3545; font-size: 0.85rem; margin-top: 5px; } @media (max-width: 480px) { .register-container { padding: 20px; } } </style> </head> <body> <div class="register-container"> <div class="register-header"> <h1>Create Account</h1> <p>Join our community today</p> </div> <form id="register-form"> <div class="form-group"> <label for="fullname" class="required">Full Name</label> <div class="input-wrapper"> <i>👤</i> <input type="text" id="fullname" name="fullname" placeholder="Enter your full name" required> </div> </div> <div class="form-group"> <label for="email" class="required">Email Address</label> <div class="input-wrapper"> <i>✉️</i> <input type="email" id="email" name="email" placeholder="[email protected]" required> </div> </div> <div class="form-group"> <label for="password" class="required">Password</label> <div class="input-wrapper"> <i>🔒</i> <input type="password" id="password" name="password" placeholder="Create a password" required> </div> <div class="password-strength"> <div class="strength-bar" id="strength-bar"></div> </div> <div class="password-requirements" id="password-reqs"> <p>Password must contain:</p> <ul> <li id="req-length">✓ At least 8 characters</li> <li id="req-lower">✓ At least 1 lowercase letter</li> <li id="req-upper">✓ At least 1 uppercase letter</li> <li id="req-number">✓ At least 1 number</li> <li id="req-special">✓ At least 1 special character</li> </ul> </div> </div> <div class="form-group"> <label for="confirm-password" class="required">Confirm Password</label> <div class="input-wrapper"> <i>🔒</i> <input type="password" id="confirm-password" name="confirm_password" placeholder="Re-enter your password" required> </div> <div id="password-match-error" class="error-message"></div> </div> <div class="checkbox-group"> <input type="checkbox" id="terms" name="terms" required> <label for="terms"> I agree to the <a href="#">Terms of Service</a> and <a href="#">Privacy Policy</a> </label> </div> <button type="submit" class="btn" id="submit-btn">Create Account</button> </form> <div class="login-link"> Already have an account? <a href="#">Sign in</a> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { const form = document.getElementById('register-form'); const password = document.getElementById('password'); const confirm = document.getElementById('confirm-password'); const submitBtn = document.getElementById('submit-btn'); // Password strength checker function checkPasswordStrength() { const pwd = password.value; const reqs = { length: pwd.length >= 8, lower: /[a-z]/.test(pwd), upper: /[A-Z]/.test(pwd), number: /\d/.test(pwd), special: /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(pwd) }; // Update requirement indicators for (const [req, met] of Object.entries(reqs)) { const element = document.getElementById(`req-${req}`); if (met) { element.style.color = '#28a745'; element.innerHTML = '✓ ' + element.innerHTML.substring(2); } else { element.style.color = '#999'; element.innerHTML = '○ ' + element.innerHTML.substring(2); } } // Calculate strength const metCount = Object.values(reqs).filter(Boolean).length; const bar = document.getElementById('strength-bar'); if (metCount <= 2) { bar.className = 'strength-bar weak'; } else if (metCount <= 4) { bar.className = 'strength-bar medium'; } else { bar.className = 'strength-bar strong'; } } // Password match checker function checkPasswordMatch() { const error = document.getElementById('password-match-error'); if (password.value !== confirm.value) { error.textContent = 'Passwords do not match'; return false; } else { error.textContent = ''; return true; } } // Real-time validation password.addEventListener('input', checkPasswordStrength); confirm.addEventListener('input', checkPasswordMatch); // Form submission form.addEventListener('submit', function(e) { e.preventDefault(); // Check if form is valid if (!form.checkValidity()) { form.reportValidity(); return; } if (!checkPasswordMatch()) { return; } // Show loading state submitBtn.disabled = true; submitBtn.textContent = 'Creating account...'; // Simulate API call setTimeout(() => { alert('Account created successfully!'); submitBtn.disabled = false; submitBtn.textContent = 'Create Account'; form.reset(); }, 2000); }); }); </script> </body> </html> Example 2: Contact Form with Validation
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Contact Form</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: #f0f2f5; padding: 20px; } .contact-container { max-width: 600px; margin: 40px auto; background: white; border-radius: 15px; box-shadow: 0 10px 30px rgba(0,0,0,0.1); overflow: hidden; } .contact-header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; text-align: center; } .contact-header h1 { font-size: 2rem; margin-bottom: 10px; } .contact-header p { opacity: 0.9; } .contact-form { padding: 30px; } .form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 20px; } .form-group { margin-bottom: 20px; } .form-group.full-width { grid-column: span 2; } label { display: block; margin-bottom: 8px; color: #333; font-weight: 500; font-size: 0.9rem; } label::after { content: ' *'; color: #ff4444; display: inline; } label.optional::after { content: ' (optional)'; color: #666; font-weight: normal; } input, select, textarea { width: 100%; padding: 12px 15px; border: 2px solid #e0e0e0; border-radius: 8px; font-size: 1rem; transition: all 0.3s; font-family: inherit; } input:focus, select:focus, textarea:focus { outline: none; border-color: #667eea; box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); } input.error, select.error, textarea.error { border-color: #ff4444; } .error-message { color: #ff4444; font-size: 0.85rem; margin-top: 5px; } .success-message { color: #28a745; font-size: 0.85rem; margin-top: 5px; } .radio-group, .checkbox-group { display: flex; gap: 20px; flex-wrap: wrap; } .radio-option, .checkbox-option { display: flex; align-items: center; gap: 8px; } .radio-option input, .checkbox-option input { width: auto; } .radio-option label, .checkbox-option label { margin: 0; font-weight: normal; } .radio-option label::after, .checkbox-option label::after { content: ''; } .file-upload { border: 2px dashed #e0e0e0; border-radius: 8px; padding: 20px; text-align: center; cursor: pointer; transition: all 0.3s; } .file-upload:hover { border-color: #667eea; background: #f8f9ff; } .file-upload input { display: none; } .file-upload label { display: flex; flex-direction: column; align-items: center; gap: 10px; cursor: pointer; margin: 0; } .file-upload i { font-size: 40px; color: #667eea; } .file-upload span { color: #667eea; font-weight: 500; } .file-info { margin-top: 10px; font-size: 0.9rem; color: #666; } .form-actions { display: flex; gap: 15px; margin-top: 30px; } .btn { flex: 1; padding: 14px; border: none; border-radius: 8px; font-size: 1rem; font-weight: 600; cursor: pointer; transition: all 0.3s; } .btn-primary { background: #667eea; color: white; } .btn-primary:hover { background: #5a67d8; transform: translateY(-2px); box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4); } .btn-secondary { background: #e0e0e0; color: #333; } .btn-secondary:hover { background: #d0d0d0; } @media (max-width: 768px) { .form-row { grid-template-columns: 1fr; gap: 0; } .form-group.full-width { grid-column: span 1; } .radio-group { flex-direction: column; gap: 10px; } .form-actions { flex-direction: column; } } </style> </head> <body> <div class="contact-container"> <div class="contact-header"> <h1>Get in Touch</h1> <p>We'd love to hear from you. Send us a message and we'll respond as soon as possible.</p> </div> <form class="contact-form" id="contactForm"> <div class="form-row"> <div class="form-group"> <label for="firstname">First Name</label> <input type="text" id="firstname" name="firstname" placeholder="John" required> <div class="error-message" id="firstname-error"></div> </div> <div class="form-group"> <label for="lastname">Last Name</label> <input type="text" id="lastname" name="lastname" placeholder="Doe" required> <div class="error-message" id="lastname-error"></div> </div> </div> <div class="form-group"> <label for="email">Email Address</label> <input type="email" id="email" name="email" placeholder="[email protected]" required> <div class="error-message" id="email-error"></div> </div> <div class="form-group"> <label for="phone">Phone Number <span class="optional">(optional)</span></label> <input type="tel" id="phone" name="phone" placeholder="(555) 123-4567"> <div class="error-message" id="phone-error"></div> </div> <div class="form-group"> <label for="subject">Subject</label> <select id="subject" name="subject" required> <option value="">Select a subject</option> <option value="general">General Inquiry</option> <option value="support">Technical Support</option> <option value="billing">Billing Question</option> <option value="partnership">Partnership Opportunity</option> </select> <div class="error-message" id="subject-error"></div> </div> <div class="form-group"> <label>Preferred Contact Method</label> <div class="radio-group"> <div class="radio-option"> <input type="radio" id="contact-email" name="contact_method" value="email" checked> <label for="contact-email">Email</label> </div> <div class="radio-option"> <input type="radio" id="contact-phone" name="contact_method" value="phone"> <label for="contact-phone">Phone</label> </div> <div class="radio-option"> <input type="radio" id="contact-any" name="contact_method" value="any"> <label for="contact-any">Any</label> </div> </div> </div> <div class="form-group full-width"> <label for="message">Message</label> <textarea id="message" name="message" rows="5" placeholder="How can we help you?" required></textarea> <div class="error-message" id="message-error"></div> <div class="character-counter"> <span id="charCount">0</span>/500 characters </div> </div> <div class="form-group full-width"> <div class="file-upload" id="fileUpload"> <input type="file" id="attachment" name="attachment" accept=".pdf,.doc,.docx,.jpg,.png"> <label for="attachment"> <i>📎</i> <span>Click to upload attachment</span> <small>or drag and drop</small> <small>PDF, DOC, JPG, PNG (max 5MB)</small> </label> </div> <div class="file-info" id="fileInfo"></div> </div> <div class="checkbox-group"> <div class="checkbox-option"> <input type="checkbox" id="newsletter" name="newsletter"> <label for="newsletter">Subscribe to our newsletter</label> </div> </div> <div class="form-actions"> <button type="submit" class="btn btn-primary" id="submitBtn">Send Message</button> <button type="reset" class="btn btn-secondary">Clear Form</button> </div> </form> </div> <script> document.addEventListener('DOMContentLoaded', function() { const form = document.getElementById('contactForm'); const submitBtn = document.getElementById('submitBtn'); const message = document.getElementById('message'); const charCount = document.getElementById('charCount'); const fileInput = document.getElementById('attachment'); const fileInfo = document.getElementById('fileInfo'); // Character counter message.addEventListener('input', function() { const count = this.value.length; charCount.textContent = count; if (count > 450) { charCount.style.color = '#ff4444'; } else if (count > 400) { charCount.style.color = '#ffbb33'; } else { charCount.style.color = '#666'; } }); // File upload handling fileInput.addEventListener('change', function() { if (this.files.length > 0) { const file = this.files[0]; const fileSize = (file.size / 1024 / 1024).toFixed(2); fileInfo.textContent = `Selected: ${file.name} (${fileSize} MB)`; } else { fileInfo.textContent = ''; } }); // Drag and drop highlight const uploadArea = document.getElementById('fileUpload'); uploadArea.addEventListener('dragover', function(e) { e.preventDefault(); this.style.borderColor = '#667eea'; this.style.background = '#f8f9ff'; }); uploadArea.addEventListener('dragleave', function(e) { e.preventDefault(); this.style.borderColor = '#e0e0e0'; this.style.background = 'white'; }); uploadArea.addEventListener('drop', function(e) { e.preventDefault(); this.style.borderColor = '#e0e0e0'; this.style.background = 'white'; const files = e.dataTransfer.files; if (files.length > 0) { fileInput.files = files; const file = files[0]; const fileSize = (file.size / 1024 / 1024).toFixed(2); fileInfo.textContent = `Selected: ${file.name} (${fileSize} MB)`; } }); // Form validation function validateField(field) { const errorElement = document.getElementById(field.id + '-error'); if (!field.checkValidity()) { field.classList.add('error'); if (field.validity.valueMissing) { errorElement.textContent = 'This field is required'; } else if (field.validity.typeMismatch) { errorElement.textContent = 'Please enter a valid ' + field.type; } else if (field.validity.tooShort) { errorElement.textContent = 'Minimum length is ' + field.minLength + ' characters'; } else { errorElement.textContent = field.validationMessage; } return false; } else { field.classList.remove('error'); errorElement.textContent = ''; return true; } } // Real-time validation form.querySelectorAll('input, select, textarea').forEach(field => { field.addEventListener('blur', function() { validateField(this); }); field.addEventListener('input', function() { if (this.classList.contains('error')) { validateField(this); } }); }); // Form submission form.addEventListener('submit', function(e) { e.preventDefault(); let isValid = true; // Validate all fields form.querySelectorAll('input[required], select[required], textarea[required]').forEach(field => { if (!validateField(field)) { isValid = false; } }); // Validate email format const email = document.getElementById('email'); if (email.value && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.value)) { document.getElementById('email-error').textContent = 'Please enter a valid email address'; email.classList.add('error'); isValid = false; } // Validate phone if provided const phone = document.getElementById('phone'); if (phone.value && !/^[\d\s\-\(\)]+$/.test(phone.value)) { document.getElementById('phone-error').textContent = 'Please enter a valid phone number'; phone.classList.add('error'); isValid = false; } if (isValid) { // Show loading state submitBtn.disabled = true; submitBtn.textContent = 'Sending...'; // Simulate form submission setTimeout(() => { alert('Message sent successfully! We\'ll get back to you soon.'); form.reset(); charCount.textContent = '0'; fileInfo.textContent = ''; submitBtn.disabled = false; submitBtn.textContent = 'Send Message'; // Clear error states form.querySelectorAll('.error').forEach(field => { field.classList.remove('error'); }); form.querySelectorAll('.error-message').forEach(msg => { msg.textContent = ''; }); }, 2000); } }); // Reset button form.querySelector('button[type="reset"]').addEventListener('click', function() { setTimeout(() => { charCount.textContent = '0'; fileInfo.textContent = ''; // Clear error states form.querySelectorAll('.error').forEach(field => { field.classList.remove('error'); }); form.querySelectorAll('.error-message').forEach(msg => { msg.textContent = ''; }); }, 0); }); }); </script> </body> </html> Best Practices
Form Design Guidelines
/* Form design best practices */ .form-best-practices { max-width: 600px; margin: 0 auto; } /* 1. Clear visual hierarchy */ .form-section { margin-bottom: 30px; padding: 20px; background: #f9f9f9; border-radius: 8px; } .form-section h3 { margin-top: 0; margin-bottom: 20px; color: #333; font-size: 1.25rem; } /* 2. Consistent spacing */ .form-group { margin-bottom: 20px; } /* 3. Clear labels */ label { display: block; margin-bottom: 5px; font-weight: 500; color: #333; } /* 4. Adequate touch targets (mobile) */ @media (max-width: 768px) { input, select, textarea, button { min-height: 44px; /* Apple's recommended minimum */ } .checkbox-group label, .radio-group label { min-height: 44px; display: flex; align-items: center; } } /* 5. Error states clearly visible */ .error-message { color: #dc3545; font-size: 0.875rem; margin-top: 5px; display: flex; align-items: center; gap: 5px; } .error-message::before { content: '⚠️'; font-size: 0.875rem; } /* 6. Success states */ .success-message { color: #28a745; font-size: 0.875rem; margin-top: 5px; display: flex; align-items: center; gap: 5px; } .success-message::before { content: '✅'; } /* 7. Loading states */ .btn.loading { position: relative; color: transparent; } .btn.loading::after { content: ''; position: absolute; width: 20px; height: 20px; top: 50%; left: 50%; margin-left: -10px; margin-top: -10px; border: 2px solid white; border-top-color: transparent; border-radius: 50%; animation: spin 1s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } } /* 8. Disabled states */ input:disabled, select:disabled, textarea:disabled, button:disabled { opacity: 0.5; cursor: not-allowed; } /* 9. Focus indicators for accessibility */ :focus { outline: 3px solid rgba(102, 126, 234, 0.4); outline-offset: 2px; } /* 10. Help text */ .help-text { color: #666; font-size: 0.875rem; margin-top: 5px; } Form Security Checklist
<!-- Form security checklist --> <div class="security-checklist"> <h3>Form Security Checklist</h3> <ul> <li> <input type="checkbox" checked disabled> ✓ Use POST method for sensitive data </li> <li> <input type="checkbox" checked disabled> ✓ Implement CSRF tokens </li> <li> <input type="checkbox" checked disabled> ✓ Validate input on server-side </li> <li> <input type="checkbox" checked disabled> ✓ Sanitize user input </li> <li> <input type="checkbox" checked disabled> ✓ Use HTTPS for form submission </li> <li> <input type="checkbox" checked disabled> ✓ Implement rate limiting </li> <li> <input type="checkbox" checked disabled> ✓ Set proper input length limits </li> <li> <input type="checkbox" checked disabled> ✓ Escape output to prevent XSS </li> <li> <input type="checkbox" checked disabled> ✓ Use prepared statements for databases </li> <li> <input type="checkbox" checked disabled> ✓ Implement CAPTCHA for public forms </li> </ul> </div>
Accessibility Checklist
<!-- Accessibility checklist --> <div class="a11y-checklist"> <h3>Accessibility Checklist</h3> <ul> <li> <input type="checkbox" checked disabled> ✓ All inputs have associated labels </li> <li> <input type="checkbox" checked disabled> ✓ Form fields are keyboard accessible </li> <li> <input type="checkbox" checked disabled> ✓ Focus indicators are visible </li> <li> <input type="checkbox" checked disabled> ✓ Error messages are announced to screen readers </li> <li> <input type="checkbox" checked disabled> ✓ Required fields are indicated </li> <li> <input type="checkbox" checked disabled> ✓ Form uses semantic HTML </li> <li> <input type="checkbox" checked disabled> ✓ Color is not the only means of conveying information </li> <li> <input type="checkbox" checked disabled> ✓ Sufficient color contrast for text </li> <li> <input type="checkbox" checked disabled> ✓ Form fields are grouped logically </li> <li> <input type="checkbox" checked disabled> ✓ ARIA attributes used when necessary </li> </ul> </div>
Conclusion
Forms are a critical component of web development, serving as the primary means of user interaction and data collection. This comprehensive guide covered:
Key Takeaways
- HTML Form Elements: Understanding all input types and their purposes
- Form Validation: Both built-in HTML5 and custom JavaScript validation
- CSS Styling: Creating beautiful, user-friendly form designs
- Accessibility: Making forms usable for everyone
- Security: Protecting user data and preventing attacks
- User Experience: Designing forms that are easy to use
- Responsive Design: Forms that work on all devices
- Real-time Validation: Providing immediate feedback
- Custom Components: Building unique form elements
- Best Practices: Following industry standards
Next Steps
- Practice building different types of forms
- Implement server-side validation
- Learn about form security in depth
- Study UX patterns for complex forms
- Experiment with CSS frameworks for forms
- Test forms with screen readers
- Optimize form performance
Remember: A well-designed form can significantly improve user experience and conversion rates. Take the time to understand your users' needs and design forms that are both functional and delightful to use!
HTML & CSS Learning Guides, Exercises, Projects, Design Systems, Accessibility & Performance (Related to HTML & CSS Development)
HTML & CSS Quiz – Comprehensive Assessment:
This quiz evaluates core knowledge of HTML and CSS including structure, styling, layout, forms, and responsive design. It is used to test practical understanding of front-end fundamentals and identify skill levels in web development.
Read more: https://macronepal.com/bash/html-and-css-quiz-comprehensive-assessment/
Complete Guide to HTML & CSS Tooling & Automation:
This guide explains tools and automation workflows used in modern web development, such as preprocessors, build tools, and task runners that improve efficiency in HTML and CSS projects.
Read more: https://macronepal.com/bash/complete-guide-to-html-and-css-tooling-automation/
Complete HTML & CSS Exercises:
A collection of practical exercises designed to strengthen HTML and CSS skills through hands-on coding tasks, covering layout, styling, and responsive design concepts.
Read more: https://macronepal.com/bash/complete-html-and-css-exercises/
Complete HTML & CSS Landing Page Project:
This project focuses on building a full landing page using HTML and CSS, helping learners understand real-world website structure, layout design, and UI development.
Read more: https://macronepal.com/bash/complete-html-css-landing-page-project/
HTML & CSS Debugging and Testing Guide:
This guide teaches how to identify and fix errors in HTML and CSS code, along with testing techniques to ensure websites work correctly across browsers.
Read more: https://macronepal.com/bash/complete-guide-to-html-and-css-debugging-testing/
HTML & CSS Design Systems Guide:
A design system approach helps maintain consistency in UI development using reusable components, styles, and patterns across web projects.
Read more: https://macronepal.com/html-and-css/complete-guide-to-html-and-css-design-systems/
HTML & CSS Accessibility (A11y) Guide:
This guide focuses on making websites accessible for all users, including proper semantic HTML, keyboard navigation, alt text, and screen reader support.
Read more: https://macronepal.com/bash/complete-guide-to-html-css-accessibility-a11y/
HTML & CSS Performance Guide:
This topic explains optimization techniques such as reducing file size, improving loading speed, and writing efficient HTML and CSS for better website performance.
Read more: https://macronepal.com/bash/complete-guide-to-html-and-css-performance/
HTML & CSS Design Systems (Advanced Overview):
Design systems help developers maintain scalable and consistent UI components across large projects using structured HTML and reusable CSS patterns.
Read more: https://macronepal.com/html-and-css/complete-guide-to-html-and-css-design-systems/