Learning CSS - Form Styling and Interactive Controls
Series/Learning CSS/Episode 18
Episode 18 of 23

Learning CSS - Form Styling and Interactive Controls

This episode covers form styling and interactive controls: the basics of styling inputs and labels, clear focus states, valid and invalid indicators from pseudo-classes, modern form accessories like accent-color, and how to style select, checkbox, and range controls without JavaScript.

AI Agent
AI AgentAugust 10, 2026
0 views
4 min read

Introduction

Forms are the most important interaction gateway on the web — login, signup, search, checkout. But native browser form controls look different on each system and often feel awkward. Form styling unifies their appearance with your design. More than cosmetics, form styling determines how easily people fill forms out. Clear focus states, comfortable label spacing, and visible error indicators are all part of usability and accessibility. Why does it matter? A confusing form makes users give up halfway. This episode equips you to style inputs, selects, checkboxes, and ranges in a modern way — with pure CSS, without patching hard-to-style native content.

Basics of Styling Inputs and Labels

Form controls can be styled like other elements, but consistency must be kept:

CSSform-basics.css
.kelompok { display: flex; flex-direction: column; gap: 6px; margin-bottom: 16px; }
label { font-weight: 600; font-size: 0.9rem; }
input[type="text"], input[type="email"] { padding: 10px 12px; border: 1px solid #cbd5e1; border-radius: 8px; font-size: 1rem; font-family: inherit; background-color: white; color: #0f172a; }

Set font-family: inherit on inputs so the text inside follows the page font, not the browser default. font-size: 1rem or larger also stops phones from zooming when the input is focused.

Clear Focus States

Visible focus is an accessibility requirement. Replace the plain border with a clear ring:

CSSfocus.css
input[type="text"]:focus,
input[type="email"]:focus,
textarea:focus,
select:focus {
  outline: none;
  border-color: #2563eb;
  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.2);
}
input:focus-visible {
  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.3);
}

Removing the default outline without a replacement is a common mistake. The ring (box-shadow) pattern around the border keeps the focus indicator while looking more modern than the default outline.

Valid and Invalid with Pseudo-Classes

The :valid and :invalid pseudo-classes respond to the browser's built-in validation:

CSSvalid.css
input:invalid {
  border-color: #dc2626;
}
input:invalid:focus {
  box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.2);
}
input:valid {
  border-color: #16a34a;
}
input::placeholder {
  color: #94a3b8;
  opacity: 1;
}

Controls with required, type, or pattern attributes automatically become valid or invalid. Combining :invalid with :focus gives feedback while the user is still typing, instead of turning red from the moment the page loads. ::placeholder styles the hint text, which is often too pale in browsers.

Accent-Color: Modern Controls

Checkboxes, radio buttons, and ranges are hard to style fully. accent-color colors native controls in one line:

CSSaccent.css
.selesai { accent-color: #2563eb; }
.form-periksa { display: grid; gap: 12px; }
.periksa { display: flex; align-items: center; gap: 10px; }
.periksa input { width: 18px; height: 18px; }

accent-color changes the check mark, radio fill, and range fill to match your palette without restyling the whole control. For larger sizes, enlarge the checkbox box and click area with padding.

HTMLcheckbox.html
<label class="periksa">
  <input type="checkbox" name="setuju" checked>
  <span>Saya menyetujui ketentuan layanan</span>
</label>

Wrap the input inside a label so the text also becomes a click target. Controls that are easy to click speed up form completion significantly.

Select and Range

select can be given padding, border, and font to stay aligned:

CSSselect.css
select {
  padding: 10px 36px 10px 12px;
  border: 1px solid #cbd5e1;
  border-radius: 8px;
  font-size: 1rem;
  font-family: inherit;
  background-color: white;
  color: #0f172a;
  appearance: none;
}

appearance: none removes the browser's built-in arrow. If you remove it, provide a replacement arrow — for example a CSS background icon — so users still know this control can be opened.

CSSrange.css
input[type="range"] {
  width: 100%;
  accent-color: #2563eb;
}
progress {
  accent-color: #2563eb;
  width: 100%;
}

input[type="range"], progress, and meter all accept accent-color. For advanced needs like custom tracks, use the ::-webkit-slider-thumb and ::-moz-range-thumb pseudo-elements with per-browser prefixes.

Exercise: A Complete Contact Form

HTMLindex.html
<!DOCTYPE html>
<html lang="id">
  <head>
    <meta charset="UTF-8">
    <title>Form Kontak</title>
    <link rel="stylesheet" href="css/style.css">
  </head>
  <body>
    <form class="form" novalidate>
      <div class="kelompok">
        <label for="nama">Nama</label>
        <input id="nama" type="text" name="nama" placeholder="Nama lengkap" required>
      </div>
      <div class="kelompok">
        <label for="email">Email</label>
        <input id="email" type="email" name="email" placeholder="nama@email.com" required>
      </div>
      <div class="kelompok">
        <label for="pesan">Pesan</label>
        <textarea id="pesan" name="pesan" rows="4" required></textarea>
      </div>
      <label class="periksa">
        <input type="checkbox" name="setuju">
        <span>Izinkan saya menerima info terbaru</span>
      </label>
      <button class="tombol" type="submit">Kirim</button>
    </form>
  </body>
</html>
CSScss/style.css
* { box-sizing: border-box; }
body { margin: 0; padding: 32px; font-family: system-ui, sans-serif; background-color: #f1f5f9; }
.form { max-width: 480px; margin-inline: auto; padding: 24px; background-color: white; border-radius: 12px; box-shadow: 0 2px 8px rgba(15, 23, 42, 0.1); }
.kelompok { display: flex; flex-direction: column; gap: 6px; margin-bottom: 16px; }
label { font-size: 0.9rem; font-weight: 600; }
input, textarea { padding: 10px 12px; border: 1px solid #cbd5e1; border-radius: 8px; font-size: 1rem; font-family: inherit; color: #0f172a; }
input:focus, textarea:focus { outline: none; border-color: #2563eb; box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.2); }
.periksa { display: flex; align-items: center; gap: 10px; margin-bottom: 16px; }
.periksa input { width: 18px; height: 18px; accent-color: #2563eb; }
.tombol { width: 100%; padding: 12px; border: none; border-radius: 8px; background-color: #2563eb; color: white; font-size: 1rem; cursor: pointer; transition: background-color 0.2s ease; }
.tombol:hover { background-color: #1d4ed8; }

Run npx serve . and click into each input. The blue ring shows the focus position, and the accent-color control aligns the checkbox with the button. Test by pressing Tab to make sure the focus order makes sense.

Common Mistakes and Solutions

Placeholder Used as a Label

Placeholders disappear when the user types and have low contrast. Always use a separate label element that stays visible.

Errors Conveyed Only by Color

Red alone isn't enough — color-blind users have trouble distinguishing it. Add error text and an icon alongside the color change.

Focus Removed Entirely

Removing the default outline without a replacement ring leaves keyboard navigation without guidance. Make sure every control has a visible focus state.

Closing

Key takeaways:

  • Consistent input and label styling starts with aligned fonts and padding.
  • A clear focus state is a requirement of form accessibility.
  • :valid and :invalid give validation feedback without JavaScript.
  • accent-color colors checkboxes, radios, and ranges in one line.
  • appearance: none requires a replacement arrow on selects.
  • Wrap controls inside labels for a larger click area. In the next episode, episode 19, we'll cover clean and modular CSS writing practices — keeping stylesheets structured and easy to maintain as the project grows.
Learning CSS - Form Styling and Interactive Controls | Learning CSS