Chapter 1 of ?
css 8 min read

CSS3 Mastery — Chapter 14: CSS Forms & Custom Form Controls

Chapter 14: CSS Forms & Custom Form Controls

Forms are the primary vehicle for user input on the web. Unstyled browser native controls often feel inconsistent. Mastering custom input fields, modern accent-color, custom checkboxes and radio buttons, custom select menus, validation state feedback (:required, :valid, :invalid), floating labels, and building accessible form layouts elevates your UI components to production standards.

14.1 Customizing Text Inputs, Textareas & accent-color

Modern CSS allows subtle styling of text inputs while standardizing checkbox/radio colors across browsers using the single line accent-color property.

<!DOCTYPE html>
<html>
<head>
  <style>
    *, *::before, *::after { box-sizing: border-box; }
    body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; margin: 0; }
    h3 { margin-top: 0; color: #38bdf8; }

    .form-box { background: #1e293b; border-radius: 12px; padding: 20px; max-width: 360px; }
    .form-group { margin-bottom: 14px; }
    label { display: block; font-size: 12px; color: #94a3b8; margin-bottom: 6px; }

    /* Custom styled input */
    input[type="text"], textarea {
      width: 100%; padding: 10px 14px; background: #0f172a;
      border: 1px solid #334155; border-radius: 8px; color: #fff; font-size: 14px;
      transition: border-color 0.2s, box-shadow 0.2s;
    }
    input[type="text"]:focus, textarea:focus {
      outline: none; border-color: #38bdf8;
      box-shadow: 0 0 10px rgba(56,189,248,0.3);
    }

    /* accent-color standardizes native checkbox/radio tinting */
    .accent-demo { accent-color: #38bdf8; }
  </style>
</head>
<body>
  <h3>Input Styling &amp; accent-color Demo</h3>

  <div class="form-box">
    <div class="form-group">
      <label>Styled Input Field:</label>
      <input type="text" placeholder="Type here..." />
    </div>

    <div class="form-group" style="display:flex;gap:16px;align-items:center;">
      <label style="margin:0;cursor:pointer;">
        <input type="checkbox" class="accent-demo" checked /> accent-color Checkbox
      </label>
      <label style="margin:0;cursor:pointer;">
        <input type="radio" class="accent-demo" checked /> accent-color Radio
      </label>
    </div>
  </div>
</body>
</html>

14.2 Custom Radio Buttons, Checkboxes & Select Menus

By using appearance: none or hiding native elements with :checked + label selectors, you can create pixel-perfect custom form controls matching any design system.

<!DOCTYPE html>
<html>
<head>
  <style>
    *, *::before, *::after { box-sizing: border-box; }
    body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; margin: 0; }
    h3 { margin-top: 0; color: #818cf8; }

    .control-group { margin-bottom: 16px; display: flex; flex-direction: column; gap: 10px; }

    /* Hidden native radio/checkbox */
    .custom-check input, .custom-radio input { display: none; }

    /* Custom Checkbox UI */
    .custom-check { display: flex; align-items: center; gap: 10px; cursor: pointer; user-select: none; }
    .check-box {
      width: 20px; height: 20px; border: 2px solid #6366f1; border-radius: 6px;
      display: flex; align-items: center; justify-content: center; transition: all 0.2s;
    }
    .custom-check input:checked + .check-box { background: #6366f1; }
    .custom-check input:checked + .check-box::after { content: "✔"; color: #fff; font-size: 12px; font-weight: bold; }

    /* Custom Radio UI */
    .custom-radio { display: flex; align-items: center; gap: 10px; cursor: pointer; user-select: none; }
    .radio-circle {
      width: 20px; height: 20px; border: 2px solid #10b981; border-radius: 50%;
      display: flex; align-items: center; justify-content: center; transition: all 0.2s;
    }
    .radio-dot { width: 10px; height: 10px; background: #10b981; border-radius: 50%; opacity: 0; transform: scale(0); transition: all 0.2s; }
    .custom-radio input:checked + .radio-circle .radio-dot { opacity: 1; transform: scale(1); }

    /* Styled Select Dropdown */
    select.custom-select {
      appearance: none;
      width: 100%; max-width: 280px; padding: 10px 36px 10px 14px;
      background: #1e293b url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%2394a3b8'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3C/svg%3E") no-repeat right 12px center / 20px;
      border: 1px solid #334155; border-radius: 8px; color: #fff; font-size: 14px; cursor: pointer;
    }
  </style>
</head>
<body>
  <h3>Custom Checkbox, Radio &amp; Select Dropdown</h3>

  <div class="control-group">
    <label class="custom-check">
      <input type="checkbox" checked />
      <div class="check-box"></div>
      <span style="font-size:14px;">Custom Checkbox (:checked state)</span>
    </label>

    <label class="custom-radio">
      <input type="radio" name="plan" checked />
      <div class="radio-circle"><div class="radio-dot"></div></div>
      <span style="font-size:14px;">Monthly Plan</span>
    </label>
    <label class="custom-radio">
      <input type="radio" name="plan" />
      <div class="radio-circle"><div class="radio-dot"></div></div>
      <span style="font-size:14px;">Annual Plan (Save 20%)</span>
    </label>

    <select class="custom-select">
      <option>Select Experience Level...</option>
      <option>Beginner (0-1 yrs)</option>
      <option>Intermediate (1-3 yrs)</option>
      <option>Advanced (3+ yrs)</option>
    </select>
  </div>
</body>
</html>

14.3 Form Validation State Feedback (:required, :valid, :invalid)

CSS state pseudo-classes (:required, :valid, :invalid) display real-time feedback borders and status checkmarks as users fill out required form fields.

<!DOCTYPE html>
<html>
<head>
  <style>
    *, *::before, *::after { box-sizing: border-box; }
    body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; margin: 0; }
    h3 { margin-top: 0; color: #f59e0b; }

    .field { margin-bottom: 14px; max-width: 320px; position: relative; }
    label { display: block; font-size: 12px; color: #94a3b8; margin-bottom: 4px; }

    input {
      width: 100%; padding: 10px 14px; background: #1e293b;
      border: 2px solid #334155; border-radius: 8px; color: #fff; font-size: 14px;
    }

    /* Green border for valid filled inputs */
    input:focus:valid { border-color: #10b981; box-shadow: 0 0 8px rgba(16,185,129,0.3); }

    /* Red border for invalid inputs */
    input:focus:invalid { border-color: #ef4444; box-shadow: 0 0 8px rgba(239,68,68,0.3); }

    /* Required asterisk via label::after */
    label.required::after { content: " *"; color: #ef4444; font-weight: bold; }
  </style>
</head>
<body>
  <h3>Form Validation Feedback Demo</h3>
  <p style="font-size:12px;color:#64748b;">Type an email address below to see live green/red border feedback:</p>

  <form onsubmit="return false;">
    <div class="field">
      <label class="required">Email Address:</label>
      <input type="email" placeholder="name@example.com" required />
    </div>

    <div class="field">
      <label class="required">Username (min 4 chars):</label>
      <input type="text" minlength="4" placeholder="alex123" required />
    </div>
  </form>
</body>
</html>

14.4 The Floating Label Pattern (:placeholder-shown & :focus-within)

The floating label design pattern moves the field label from inside the input box to floating above the input box as soon as the user starts typing, using :placeholder-shown or :focus-within.

<!DOCTYPE html>
<html>
<head>
  <style>
    *, *::before, *::after { box-sizing: border-box; }
    body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; margin: 0; }
    h3 { margin-top: 0; color: #10b981; }

    /* Floating Label Container */
    .float-group {
      position: relative;
      margin-bottom: 20px;
      max-width: 320px;
    }

    .float-input {
      width: 100%; padding: 16px 14px 6px;
      background: #1e293b; border: 2px solid #334155;
      border-radius: 8px; color: #fff; font-size: 14px;
      transition: border-color 0.2s;
    }

    .float-label {
      position: absolute; top: 12px; left: 14px;
      color: #64748b; font-size: 14px; pointer-events: none;
      transition: all 0.2s ease;
    }

    /* When input is focused OR has value (placeholder NOT shown) -> float label up! */
    .float-input:focus + .float-label,
    .float-input:not(:placeholder-shown) + .float-label {
      top: 4px; left: 14px;
      font-size: 10px; color: #38bdf8; font-weight: 700;
    }

    .float-input:focus { outline: none; border-color: #38bdf8; }
  </style>
</head>
<body>
  <h3>Pure CSS Floating Label Pattern</h3>

  <div class="float-group">
    <input type="text" class="float-input" id="name" placeholder=" " />
    <label for="name" class="float-label">Full Name</label>
  </div>

  <div class="float-group">
    <input type="email" class="float-input" id="email" placeholder=" " />
    <label for="email" class="float-label">Email Address</label>
  </div>
</body>
</html>

14.5 Mini-Project #1: Accessible Floating Label Contact Form

Putting everything together into a production-grade, accessible contact form featuring floating labels, custom checkboxes, custom radios, select dropdowns, and validation states:

<!DOCTYPE html>
<html>
<head>
  <style>
    *, *::before, *::after { box-sizing: border-box; }
    body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; margin: 0; }

    .form-card {
      background: #1e293b; border: 1px solid #334155; border-radius: 16px;
      padding: 28px; max-width: 440px; margin: 0 auto;
      box-shadow: 0 12px 32px rgba(0,0,0,0.4);
    }
    .form-title { margin-top: 0; color: #38bdf8; font-size: 1.4rem; }

    /* Floating input */
    .float-group { position: relative; margin-bottom: 16px; }
    .float-input, .float-textarea {
      width: 100%; padding: 16px 14px 6px;
      background: #0f172a; border: 2px solid #334155; border-radius: 8px;
      color: #fff; font-size: 14px; transition: all 0.2s;
    }
    .float-textarea { height: 80px; resize: vertical; }
    .float-label {
      position: absolute; top: 12px; left: 14px;
      color: #64748b; font-size: 14px; pointer-events: none; transition: all 0.2s;
    }
    .float-input:focus + .float-label, .float-input:not(:placeholder-shown) + .float-label,
    .float-textarea:focus + .float-label, .float-textarea:not(:placeholder-shown) + .float-label {
      top: 4px; left: 14px; font-size: 10px; color: #38bdf8; font-weight: 700;
    }
    .float-input:focus, .float-textarea:focus { outline: none; border-color: #38bdf8; }

    /* Submit Button */
    .btn-submit {
      width: 100%; background: linear-gradient(135deg, #4f46e5, #06b6d4);
      color: #fff; padding: 12px; border: none; border-radius: 8px;
      font-weight: 700; font-size: 15px; cursor: pointer;
      box-shadow: 0 4px 14px rgba(79,70,229,0.4); transition: transform 0.2s;
    }
    .btn-submit:hover { transform: translateY(-2px); }
  </style>
</head>
<body>
  <div class="form-card">
    <h3 class="form-title">Contact Us</h3>
    <form onsubmit="alert('Message Sent!'); return false;">
      <div class="float-group">
        <input type="text" class="float-input" id="c-name" placeholder=" " required />
        <label for="c-name" class="float-label">Your Name</label>
      </div>

      <div class="float-group">
        <input type="email" class="float-input" id="c-email" placeholder=" " required />
        <label for="c-email" class="float-label">Email Address</label>
      </div>

      <div class="float-group">
        <textarea class="float-textarea" id="c-msg" placeholder=" " required></textarea>
        <label for="c-msg" class="float-label">Your Message</label>
      </div>

      <button type="submit" class="btn-submit">Send Message &rarr;</button>
    </form>
  </div>
</body>
</html>

14.6 Interactive Sandbox — Forms & Floating Labels

Test custom checkboxes, toggle switches, and floating labels live in the code playground below:

Live Playground — Chapter 14 Sandbox
Done with this chapter?
Mark it complete to track your progress and unlock your certificate.
Next Up

Learner Reviews

Write a Review
Share your experience to help other learners.
Your Rating *