← Back to all tutorials

Introduction

An overview of styling HTML5 forms — why default browser styles fall short, the approach we will take, and what you will build in this series.

Introduction

Welcome to the Styling HTML 5 Forms tutorial! HTML form elements — inputs, checkboxes, radio buttons, select boxes — are essential for user interaction, but their default browser styles are often ugly, inconsistent, and outdated. In this series you will learn how to completely restyle every form element using pure CSS.

The Problem with Default Form Styles

Every browser renders form elements differently. A checkbox in Chrome looks different from Firefox, which looks different from Safari. The default styles are functional but visually bland and often clash with a modern website design.

IssueDescription
Inconsistent across browsersChrome, Firefox, and Safari all render form elements differently
Outdated appearanceDefault styles look like they belong in the early 2000s
Hard to customizeSome elements (select, radio, checkbox) resist CSS styling
Clash with modern designDefault form elements break the visual consistency of a polished site
No visual feedbackValidation states (valid/invalid) have minimal default styling

Our Approach

Rather than fighting with browser defaults, we will use a proven technique for each element type:

ElementTechnique
Radio buttonsHide the native input, style a custom circle using pseudo-elements and the :checked selector
CheckboxesHide the native input, style a custom box with a CSS checkmark using pseudo-elements
Text inputsReset browser defaults with appearance: none, apply custom borders, padding, focus states
Select boxesReset the native appearance, add a custom dropdown arrow, style the closed state
ValidationUse :valid, :invalid, :focus, and :placeholder-shown pseudo-classes for visual feedback

Key CSS Techniques You Will Learn

  • appearance: none — removes all browser default styling from form elements
  • ::before and ::after pseudo-elements — creates custom visual indicators without extra HTML
  • :checked — styles the active state of radio buttons and checkboxes
  • :focus and :focus-visible — provides visual feedback when an element is focused
  • :valid and :invalid — styles elements based on HTML5 validation status
  • :placeholder-shown — detects whether the placeholder text is visible
  • transition — smooth animations between states (unchecked → checked, unfocused → focused)

The Base HTML Form

Here is the form we will style throughout this series:

<form class="styled-form" novalidate>
    <div class="form-group">
        <label for="name">Full Name</label>
        <input type="text" id="name" placeholder="John Doe" required>
    </div>

    <div class="form-group">
        <label for="email">Email</label>
        <input type="email" id="email" placeholder="john@example.com" required>
    </div>

    <div class="form-group">
        <label>Gender</label>
        <div class="radio-group">
            <label class="radio-label">
                <input type="radio" name="gender" value="male">
                <span class="radio-custom"></span>
                Male
            </label>
            <label class="radio-label">
                <input type="radio" name="gender" value="female">
                <span class="radio-custom"></span>
                Female
            </label>
        </div>
    </div>

    <div class="form-group">
        <label class="checkbox-label">
            <input type="checkbox" id="terms">
            <span class="checkbox-custom"></span>
            I agree to the terms
        </label>
    </div>

    <div class="form-group">
        <label for="country">Country</label>
        <select id="country">
            <option value="">Select a country</option>
            <option value="us">United States</option>
            <option value="uk">United Kingdom</option>
            <option value="in">India</option>
        </select>
    </div>

    <button type="submit">Submit</button>
</form>

Base Form Styles

.styled-form {
    max-width: 480px;
    margin: 40px auto;
    padding: 32px;
    background: #fff;
    border-radius: 12px;
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
    font-family: 'Segoe UI', system-ui, sans-serif;
}

.form-group {
    margin-bottom: 24px;
}

.form-group > label {
    display: block;
    margin-bottom: 8px;
    font-size: 14px;
    font-weight: 600;
    color: #374151;
}

button[type="submit"] {
    width: 100%;
    padding: 12px;
    background: #4A90D9;
    color: white;
    border: none;
    border-radius: 8px;
    font-size: 16px;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.3s ease;
}

button[type="submit"]:hover {
    background: #357ABD;
}

This gives us a clean card layout with consistent spacing. In the following episodes, we will style each form element individually.

Prerequisites

  • Solid understanding of HTML and CSS
  • Familiarity with CSS selectors, pseudo-classes, and pseudo-elements
  • Basic understanding of CSS transitions
  • A code editor and modern browser

Key Takeaways

  • Default browser form styles are inconsistent, outdated, and hard to customize
  • The key technique is hiding the native element and replacing it with styled pseudo-elements
  • appearance: none strips browser defaults so you can restyle from scratch
  • Pseudo-classes like :checked, :focus, :valid, and :invalid provide state-based styling
  • Every technique in this series uses pure CSS — no JavaScript required