Create modern, accessible forms with validation and responsive design.
This project will guide you through creating modern, accessible forms using CSS. You'll learn to design form layouts, input styling, validation states, and responsive behavior.
Beginner: 2-3 hours | Intermediate: 1-2 hours
Difficulty Level: Beginner to Intermediate
<div class="form-container">
<div class="form-header">
<h1>Contact Us</h1>
<p>Get in touch with our team</p>
</div>
<form class="contact-form" novalidate>
<div class="form-group">
<label for="name">Full Name *</label>
<input type="text" id="name" name="name" required>
<span class="error-message">Please enter your full name</span>
</div>
<div class="form-group">
<label for="email">Email Address *</label>
<input type="email" id="email" name="email" required>
<span class="error-message">Please enter a valid email address</span>
</div>
<div class="form-group">
<label for="phone">Phone Number</label>
<input type="tel" id="phone" name="phone">
</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="other">Other</option>
</select>
<span class="error-message">Please select a subject</span>
</div>
<div class="form-group">
<label for="message">Message *</label>
<textarea id="message" name="message" rows="5" required></textarea>
<span class="error-message">Please enter your message</span>
</div>
<div class="form-group checkbox-group">
<label class="checkbox-label">
<input type="checkbox" name="newsletter">
<span class="checkmark"></span>
Subscribe to our newsletter
</label>
</div>
<button type="submit" class="submit-btn">
<i class="fas fa-paper-plane"></i>
Send Message
</button>
</form>
</div>
.form-container {
max-width: 600px;
margin: 2rem auto;
background: white;
border-radius: 20px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
overflow: hidden;
}
.form-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 2rem;
text-align: center;
}
.form-header h1 {
margin-bottom: 0.5rem;
font-size: 2rem;
}
.form-header p {
opacity: 0.9;
margin: 0;
}
.contact-form {
padding: 2rem;
}
.form-group {
margin-bottom: 1.5rem;
position: relative;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
color: #333;
}
.form-group input,
.form-group select,
.form-group textarea {
width: 100%;
padding: 0.75rem 1rem;
border: 2px solid #e9ecef;
border-radius: 10px;
font-size: 1rem;
transition: all 0.3s;
background: white;
}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
.form-group input:invalid:not(:placeholder-shown),
.form-group select:invalid:not(:placeholder-shown),
.form-group textarea:invalid:not(:placeholder-shown) {
border-color: #e74c3c;
}
.error-message {
display: none;
color: #e74c3c;
font-size: 0.8rem;
margin-top: 0.25rem;
}
.form-group.error .error-message {
display: block;
}
.form-group.error input,
.form-group.error select,
.form-group.error textarea {
border-color: #e74c3c;
}
.checkbox-group {
display: flex;
align-items: center;
}
.checkbox-label {
display: flex;
align-items: center;
cursor: pointer;
font-weight: normal;
}
.checkbox-label input[type="checkbox"] {
display: none;
}
.checkmark {
width: 20px;
height: 20px;
border: 2px solid #e9ecef;
border-radius: 4px;
margin-right: 0.75rem;
position: relative;
transition: all 0.3s;
}
.checkbox-label input[type="checkbox"]:checked + .checkmark {
background: #667eea;
border-color: #667eea;
}
.checkbox-label input[type="checkbox"]:checked + .checkmark::after {
content: '✓';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
font-size: 12px;
font-weight: bold;
}
.submit-btn {
width: 100%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
padding: 1rem 2rem;
border-radius: 10px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
}
.submit-btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}
.submit-btn:active {
transform: translateY(0);
}
.submit-btn:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
/* Success state */
.form-group.success input,
.form-group.success select,
.form-group.success textarea {
border-color: #28a745;
}
/* Responsive design */
@media (max-width: 768px) {
.form-container {
margin: 1rem;
border-radius: 15px;
}
.contact-form {
padding: 1.5rem;
}
.form-header {
padding: 1.5rem;
}
.form-header h1 {
font-size: 1.5rem;
}
}
class FormValidator {
constructor() {
this.form = document.querySelector('.contact-form');
this.init();
}
init() {
this.bindEvents();
}
bindEvents() {
this.form.addEventListener('submit', (e) => this.handleSubmit(e));
// Real-time validation
this.form.querySelectorAll('input, select, textarea').forEach(field => {
field.addEventListener('blur', () => this.validateField(field));
field.addEventListener('input', () => this.clearError(field));
});
}
validateField(field) {
const formGroup = field.closest('.form-group');
let isValid = true;
// Remove previous states
formGroup.classList.remove('error', 'success');
// Required field validation
if (field.hasAttribute('required') && !field.value.trim()) {
this.showError(formGroup, field.dataset.error || 'This field is required');
isValid = false;
}
// Email validation
if (field.type === 'email' && field.value) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(field.value)) {
this.showError(formGroup, 'Please enter a valid email address');
isValid = false;
}
}
// Phone validation
if (field.type === 'tel' && field.value) {
const phoneRegex = /^[\+]?[1-9][\d]{0,15}$/;
if (!phoneRegex.test(field.value.replace(/\s/g, ''))) {
this.showError(formGroup, 'Please enter a valid phone number');
isValid = false;
}
}
// Show success state if valid
if (isValid && field.value.trim()) {
formGroup.classList.add('success');
}
return isValid;
}
showError(formGroup, message) {
formGroup.classList.add('error');
const errorElement = formGroup.querySelector('.error-message');
if (errorElement) {
errorElement.textContent = message;
}
}
clearError(field) {
const formGroup = field.closest('.form-group');
formGroup.classList.remove('error');
}
handleSubmit(e) {
e.preventDefault();
let isValid = true;
const fields = this.form.querySelectorAll('input, select, textarea');
// Validate all fields
fields.forEach(field => {
if (!this.validateField(field)) {
isValid = false;
}
});
if (isValid) {
this.submitForm();
} else {
// Focus on first error
const firstError = this.form.querySelector('.form-group.error input, .form-group.error select, .form-group.error textarea');
if (firstError) {
firstError.focus();
}
}
}
submitForm() {
const submitBtn = this.form.querySelector('.submit-btn');
const originalText = submitBtn.innerHTML;
// Show loading state
submitBtn.disabled = true;
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Sending...';
// Simulate form submission
setTimeout(() => {
// Show success message
this.showSuccessMessage();
// Reset form
this.form.reset();
this.form.querySelectorAll('.form-group').forEach(group => {
group.classList.remove('success');
});
// Reset button
submitBtn.disabled = false;
submitBtn.innerHTML = originalText;
}, 2000);
}
showSuccessMessage() {
const successDiv = document.createElement('div');
successDiv.className = 'success-message';
successDiv.innerHTML = `
<div style="background: #d4edda; color: #155724; padding: 1rem; border-radius: 8px; margin-top: 1rem; text-align: center;">
<i class="fas fa-check-circle"></i>
Thank you! Your message has been sent successfully.
</div>
`;
this.form.appendChild(successDiv);
// Remove success message after 5 seconds
setTimeout(() => {
successDiv.remove();
}, 5000);
}
}
new FormValidator();