ESC

Type to search the knowledge base.

Pseudo-classes Hover Focus Focus-visible

Interactive pseudo-classes — :hover, :focus, :focus-visible, :active, :focus-within — with keyboard-safe patterns and touch caveats.

beginner3 min read
  • css
  • pseudo-classes
  • focus

Pseudo-classes style elements in a state. For interactive UI the core set is :hover, :focus, :focus-visible, :active, and :focus-within. Mixing them up is how you ship sticky mouse rings or invisible keyboard focus.

Docs: MDN pseudo-classes, :focus-visible, :focus-within.

Quick map

Pseudo-class When it matches
:hover Pointer over element (and often ancestors)
:active During activation (click/press)
:focus Element has focus
:focus-visible Focus should be visibly indicated (UA heuristic; typically keyboard)
:focus-within Element or descendant has focus
.button {
  background: var(--brand);
  transition: background 120ms ease, transform 80ms ease;
}

.button:hover {
  background: var(--brand-hover);
}

.button:active {
  transform: translateY(1px);
}

.button:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

:focus vs :focus-visible

/* Shows for mouse clicks too in many cases */
input:focus {
  border-color: var(--brand);
}

/* Prefer for keyboard rings */
input:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 1px;
}

Product pattern:

  1. Subtle border/background on :focus for inputs (always clear which field is active).
  2. Strong ring on :focus-visible for buttons/links.
  3. Never outline: none without a visible replacement.

:focus-within for groups

.field {
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  padding: 0.5rem 0.75rem;
}

.field:focus-within {
  border-color: var(--brand);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 25%, transparent);
}
<label class="field">
  <span>Email</span>
  <input type="email" name="email" />
</label>

Styles the wrapper when the nested control is focused — cleaner than restyling only the raw input.

Hover on touch devices

:hover can stick after tap on some devices or never apply. Essential actions must work without hover. Progressive enhancement:

@media (hover: hover) and (pointer: fine) {
  .card:hover {
    transform: translateY(-2px);
  }
}

Order and LVHA/focus

Classic link order advice (LoVe HAte) still avoids override surprises:

a:link { }
a:visited { }
a:hover { }
a:focus-visible { }
a:active { }

Interview out-loud

“:hover is pointer-over, :active is pressed, :focus means focused, :focus-visible is when a focus indicator should show — usually keyboard. :focus-within styles parents of focused descendants. I don’t rely on hover for essential UI and I never remove outlines without a :focus-visible style.”

Footguns

  1. Global *:focus { outline: none }.
  2. Hover-only disclosure menus without keyboard paths.
  3. Forgetting :focus-visible on custom tabindex="0" widgets.
  4. Using :focus rings that fail contrast on brand backgrounds.
  5. Assuming hover equals “mouse user” forever on hybrid devices.

Button state matrix

.button {
  background: var(--brand);
  color: white;
  transition: background 120ms ease, box-shadow 120ms ease, transform 80ms ease;
}
.button:hover { background: var(--brand-hover); }
.button:active { transform: translateY(1px); }
.button:disabled,
.button[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
  transform: none;
}
.button:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--brand) 30%, transparent);
}

Disable hover lift on disabled controls. Ensure :disabled styles win over :hover by source order or specificity.

Cards and whole-hit targets

When an entire card is clickable, prefer a real link stretched with CSS (pseudo-element overlay) rather than a div with click handlers. Focus styles must still show on the actual focusable element. :focus-within on the card can highlight the container while the inner link receives focus — a pattern that keeps semantics intact without hover-only affordances.

Further reading

Related guides