CSS

Lesson 9: Forms & User Interface

Duration: 50 minutes | Difficulty: Advanced | Prerequisites: Lessons 1-8 completed

πŸ“‹ Learning Objectives

By the end of this lesson, you will be able to:

🎯 Form Styling Fundamentals

Forms are crucial for user interaction. Good form design improves usability and conversion rates.

Basic Input Styling

/* Reset default styles */
input, textarea, select, button {
    font-family: inherit;
    font-size: inherit;
    border: none;
    outline: none;
    background: none;
}

/* Base input styles */
.form-input {
    width: 100%;
    padding: 12px 16px;
    border: 2px solid #e5e7eb;
    border-radius: 8px;
    font-size: 1rem;
    background: white;
    transition: all 0.2s ease;
}

.form-input:focus {
    border-color: #3b82f6;
    box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}

.form-input:disabled {
    background: #f3f4f6;
    color: #9ca3af;
    cursor: not-allowed;
}

.form-input::placeholder {
    color: #9ca3af;
}

Labels and Form Groups

.form-group {
    margin-bottom: 1.5rem;
}

.form-label {
    display: block;
    margin-bottom: 0.5rem;
    font-weight: 600;
    color: #374151;
    font-size: 0.875rem;
}

.form-label.required::after {
    content: ' *';
    color: #ef4444;
}

.form-help {
    font-size: 0.875rem;
    color: #6b7280;
    margin-top: 0.25rem;
}

.form-error {
    font-size: 0.875rem;
    color: #ef4444;
    margin-top: 0.25rem;
    display: flex;
    align-items: center;
    gap: 0.25rem;
}

.form-error::before {
    content: '⚠️';
}

πŸ”§ Input Types and Styling

Text Inputs

.input-with-icon {
    position: relative;
}

.input-with-icon input {
    padding-left: 3rem;
}

.input-with-icon .icon {
    position: absolute;
    left: 1rem;
    top: 50%;
    transform: translateY(-50%);
    color: #9ca3af;
    pointer-events: none;
}

.input-with-icon input:focus + .icon {
    color: #3b82f6;
}

Textarea Styling

.form-textarea {
    min-height: 120px;
    resize: vertical;
    line-height: 1.5;
}

.form-textarea:focus {
    border-color: #3b82f6;
    box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}

Select Dropdown

.form-select {
    appearance: none;
    background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6,9 12,15 18,9'%3e%3c/polyline%3e%3c/svg%3e");
    background-repeat: no-repeat;
    background-position: right 12px center;
    background-size: 16px;
    padding-right: 3rem;
}

.form-select:focus {
    border-color: #3b82f6;
    box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}

Checkbox and Radio Styling

/* Hide default checkbox/radio */
.custom-checkbox input,
.custom-radio input {
    position: absolute;
    opacity: 0;
    cursor: pointer;
}

/* Custom checkbox */
.custom-checkbox {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    cursor: pointer;
    user-select: none;
}

.checkbox-mark {
    width: 20px;
    height: 20px;
    border: 2px solid #d1d5db;
    border-radius: 4px;
    position: relative;
    transition: all 0.2s ease;
    flex-shrink: 0;
}

.custom-checkbox input:checked ~ .checkbox-mark {
    background: #3b82f6;
    border-color: #3b82f6;
}

.checkbox-mark::after {
    content: '';
    position: absolute;
    left: 6px;
    top: 2px;
    width: 6px;
    height: 10px;
    border: solid white;
    border-width: 0 2px 2px 0;
    transform: rotate(45deg);
    opacity: 0;
    transition: opacity 0.2s ease;
}

.custom-checkbox input:checked ~ .checkbox-mark::after {
    opacity: 1;
}

/* Custom radio */
.custom-radio {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    cursor: pointer;
    user-select: none;
}

.radio-mark {
    width: 20px;
    height: 20px;
    border: 2px solid #d1d5db;
    border-radius: 50%;
    position: relative;
    transition: all 0.2s ease;
    flex-shrink: 0;
}

.custom-radio input:checked ~ .radio-mark {
    border-color: #3b82f6;
}

.radio-mark::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: #3b82f6;
    transform: translate(-50%, -50%) scale(0);
    transition: transform 0.2s ease;
}

.custom-radio input:checked ~ .radio-mark::after {
    transform: translate(-50%, -50%) scale(1);
}

πŸ› οΈ Hands-On Exercises

Exercise 1: Complete Form Design

<form class="contact-form">
    <div class="form-row">
        <div class="form-group">
            <label class="form-label required" for="firstName">First Name</label>
            <input type="text" id="firstName" class="form-input" required>
        </div>
        
        <div class="form-group">
            <label class="form-label required" for="lastName">Last Name</label>
            <input type="text" id="lastName" class="form-input" required>
        </div>
    </div>
    
    <div class="form-group">
        <label class="form-label required" for="email">Email Address</label>
        <div class="input-with-icon">
            <input type="email" id="email" class="form-input" required>
            <span class="icon">πŸ“§</span>
        </div>
    </div>
    
    <div class="form-group">
        <label class="form-label" for="message">Message</label>
        <textarea id="message" class="form-input form-textarea" placeholder="Your message here..."></textarea>
        <div class="form-help">Maximum 500 characters</div>
    </div>
    
    <div class="form-group">
        <div class="custom-checkbox">
            <input type="checkbox" id="newsletter">
            <div class="checkbox-mark"></div>
            <label for="newsletter">Subscribe to our newsletter</label>
        </div>
    </div>
    
    <div class="form-actions">
        <button type="submit" class="btn btn-primary">Send Message</button>
        <button type="reset" class="btn btn-secondary">Clear Form</button>
    </div>
</form>
.contact-form {
    max-width: 600px;
    margin: 2rem auto;
    padding: 2rem;
    background: white;
    border-radius: 12px;
    box-shadow: 0 4px 6px rgba(0,0,0,0.07);
}

.form-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1rem;
}

.form-actions {
    display: flex;
    gap: 1rem;
    justify-content: flex-end;
    margin-top: 2rem;
}

.btn {
    padding: 12px 24px;
    border-radius: 8px;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.2s ease;
    border: 2px solid transparent;
}

.btn-primary {
    background: #3b82f6;
    color: white;
}

.btn-primary:hover {
    background: #2563eb;
    transform: translateY(-1px);
}

.btn-secondary {
    background: transparent;
    color: #6b7280;
    border-color: #d1d5db;
}

.btn-secondary:hover {
    background: #f3f4f6;
    border-color: #9ca3af;
}

@media (max-width: 640px) {
    .form-row {
        grid-template-columns: 1fr;
    }
    
    .form-actions {
        flex-direction: column;
    }
}

Exercise 2: Toggle Switch Component

<div class="toggle-group">
    <label class="toggle-switch">
        <input type="checkbox" class="toggle-input">
        <div class="toggle-slider"></div>
        <span class="toggle-label">Dark Mode</span>
    </label>
    
    <label class="toggle-switch">
        <input type="checkbox" class="toggle-input" checked>
        <div class="toggle-slider"></div>
        <span class="toggle-label">Notifications</span>
    </label>
</div>
.toggle-switch {
    display: flex;
    align-items: center;
    gap: 1rem;
    cursor: pointer;
    user-select: none;
    padding: 0.5rem 0;
}

.toggle-input {
    position: absolute;
    opacity: 0;
}

.toggle-slider {
    position: relative;
    width: 50px;
    height: 24px;
    background: #d1d5db;
    border-radius: 24px;
    transition: all 0.3s ease;
}

.toggle-slider::before {
    content: '';
    position: absolute;
    top: 2px;
    left: 2px;
    width: 20px;
    height: 20px;
    background: white;
    border-radius: 50%;
    transition: all 0.3s ease;
    box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}

.toggle-input:checked + .toggle-slider {
    background: #3b82f6;
}

.toggle-input:checked + .toggle-slider::before {
    transform: translateX(26px);
}

.toggle-input:focus + .toggle-slider {
    box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}

.toggle-label {
    font-weight: 500;
    color: #374151;
}

Exercise 3: Custom Dropdown

<div class="custom-dropdown">
    <button class="dropdown-trigger" type="button">
        Choose Option
        <span class="dropdown-arrow">β–Ό</span>
    </button>
    <div class="dropdown-menu">
        <a href="#" class="dropdown-item">Option 1</a>
        <a href="#" class="dropdown-item">Option 2</a>
        <a href="#" class="dropdown-item">Option 3</a>
        <div class="dropdown-divider"></div>
        <a href="#" class="dropdown-item">Other Option</a>
    </div>
</div>
.custom-dropdown {
    position: relative;
    display: inline-block;
}

.dropdown-trigger {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 200px;
    padding: 12px 16px;
    background: white;
    border: 2px solid #e5e7eb;
    border-radius: 8px;
    cursor: pointer;
    transition: all 0.2s ease;
}

.dropdown-trigger:hover {
    border-color: #3b82f6;
}

.dropdown-trigger.active {
    border-color: #3b82f6;
    box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}

.dropdown-arrow {
    transition: transform 0.2s ease;
    font-size: 0.75rem;
}

.dropdown-trigger.active .dropdown-arrow {
    transform: rotate(180deg);
}

.dropdown-menu {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background: white;
    border: 2px solid #e5e7eb;
    border-radius: 8px;
    box-shadow: 0 10px 25px rgba(0,0,0,0.1);
    z-index: 100;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-10px);
    transition: all 0.2s ease;
    margin-top: 4px;
}

.custom-dropdown.active .dropdown-menu {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}

.dropdown-item {
    display: block;
    padding: 12px 16px;
    color: #374151;
    text-decoration: none;
    transition: background-color 0.2s ease;
}

.dropdown-item:hover {
    background: #f3f4f6;
}

.dropdown-divider {
    height: 1px;
    background: #e5e7eb;
    margin: 0.25rem 0;
}

Exercise 4: Form Validation States

/* Success state */
.form-input.valid {
    border-color: #10b981;
}

.form-input.valid:focus {
    border-color: #10b981;
    box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.1);
}

.form-success {
    color: #10b981;
    font-size: 0.875rem;
    margin-top: 0.25rem;
    display: flex;
    align-items: center;
    gap: 0.25rem;
}

.form-success::before {
    content: 'βœ“';
}

/* Error state */
.form-input.invalid {
    border-color: #ef4444;
}

.form-input.invalid:focus {
    border-color: #ef4444;
    box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
}

/* Loading state */
.form-input.loading {
    background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3e%3cpath fill='%236b7280' d='M12,4a8,8,0,0,1,7.89,6.7A1.53,1.53,0,0,0,21.38,12h0a1.5,1.5,0,0,0,1.48-1.75,11,11,0,0,0-21.72,0A1.5,1.5,0,0,0,2.62,12h0a1.53,1.53,0,0,0,1.49-1.3A8,8,0,0,1,12,4Z'%3e%3canimateTransform attributeName='transform' dur='0.75s' repeatCount='indefinite' type='rotate' values='0 12 12;360 12 12'/%3e%3c/path%3e%3c/svg%3e");
    background-repeat: no-repeat;
    background-position: right 12px center;
    background-size: 16px;
    padding-right: 3rem;
}

🚨 Common Pitfalls

  1. Poor accessibility: Missing labels, focus states, or ARIA attributes
  2. Inconsistent styling: Different inputs looking different
  3. Bad error handling: Unclear or missing validation feedback
  4. Mobile unfriendly: Touch targets too small or inputs not optimized
  5. Performance issues: Complex custom components causing lag

πŸ” Best Practices

  1. Always provide labels for form inputs
  2. Design clear focus states for keyboard navigation
  3. Use semantic HTML before adding custom styling
  4. Test with real users and assistive technologies
  5. Keep forms simple and only ask for necessary information

πŸ“ Assessment

Quick Quiz

  1. Why should you always include labels with form inputs?
  2. What CSS property removes the default appearance of select elements?
  3. How do you create accessible focus states?
  4. What makes a good error message in forms?

Practice Tasks

  1. Style a complete registration form with validation
  2. Create a custom file upload component
  3. Build an accessible multi-step form
  4. Design a settings panel with various input types

πŸ”— What’s Next?

In Lesson 10, we’ll put everything together in a Capstone Project, building a complete responsive website that demonstrates all the CSS skills learned throughout this course.


Well-designed forms are crucial for user experience. Focus on clarity, accessibility, and visual consistency to create forms users love to use.