learn.colinkim.dev

Styling forms and interactive states

Learn hover, focus, active, disabled, required, invalid, checked, placeholder-shown, and form styling that preserves semantics.

Forms are semantic HTML. CSS should make them clear without hiding their meaning.

Start with real form controls:

<label for="email">Email</label>
<input id="email" name="email" type="email" required />

Then style the controls:

label {
  display: block;
  font-weight: 600;
}

input {
  inline-size: 100%;
  padding: 0.75rem;
  border: 1px solid rgb(170 180 195);
  border-radius: 0.375rem;
  font: inherit;
}

Interaction states

Buttons and links need state styles:

.button {
  background: royalblue;
  color: white;
  border: 0;
  border-radius: 0.375rem;
  padding: 0.75rem 1rem;
}

.button:hover {
  background: rgb(35 80 190);
}

.button:active {
  translate: 0 1px;
}

.button:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

Hover is not universal. Touch devices may not have hover, so do not make essential information hover-only.

Focus and focus-visible

Focus shows which control receives keyboard input:

:focus-visible {
  outline: 3px solid color-mix(in oklab, royalblue, white 25%);
  outline-offset: 3px;
}

Use :focus-visible for keyboard-friendly focus styles that do not show on every mouse click.

Validation states

CSS can respond to form validity:

input:user-invalid {
  border-color: rgb(180 35 35);
}

input:user-valid {
  border-color: rgb(20 130 85);
}

:user-invalid and :user-valid avoid showing error styles before the user interacts in supporting browsers.

Fallback:

input:invalid:not(:placeholder-shown) {
  border-color: rgb(180 35 35);
}

Other useful states:

input:required {
  border-inline-start-width: 4px;
}

input:checked + label {
  font-weight: 700;
}

Accent color

For native checkboxes, radios, and range controls:

:root {
  accent-color: royalblue;
}

This keeps native controls recognizable while matching your theme.

Placeholder text

Placeholder text is not a label:

input::placeholder {
  color: rgb(110 120 135);
}

Use placeholders for examples or hints, not required instructions.

What to carry forward

  • use semantic form controls first
  • keep labels visible or accessibly available
  • style hover, active, disabled, and focus states intentionally
  • :focus-visible supports keyboard users
  • validation styles should avoid yelling before interaction
  • accent-color is a low-risk way to theme native controls

Quick Check

One answer

Why is `:focus-visible` useful?

Choose the best answer and use it to track your progress through the lesson.

The next lesson broadens state styling into accessibility: contrast, motion preferences, forced colors, and pointer behavior.

Progress

Quick checks

No quick checks in this lesson.

Mark lesson manually or answer quick checks to track progress.