CSS · Selectors & Effects

Pseudo-Classes

Pseudo-classes style elements based on their state or position: a button being hovered, a field being focused, every second row in a table.

Intermediate 5 min Lesson 15 of 26 Runnable examples
Before you start:Selectors
0/26 in CSS
On this page · 5 min · Intermediate

Learning objectives#

  • Style interaction states: hover, focus, active
  • Style form states: checked, disabled, invalid
  • Select by position with :first-child and :nth-child()
  • Use :not(), :is() and :has()

Explanation#

A pseudo-class starts with a single colon and describes a state: a:hover, input:focus, button:disabled.

Interaction: :hover (mouse over), :focus (selected for typing), :focus-visible (focused by keyboard), :active (being pressed).

Structure: :first-child, :last-child, :nth-child(2), :nth-child(odd), :nth-child(3n) select by position among siblings.

Logic: :not(.active) excludes; :is(h1, h2, h3) groups; :has(img) selects a parent that contains an image — CSS’s long-awaited “parent selector”.

Link order matters: :link, :visited, :hover, :active (remember “LoVe HAte”).

Code example: States and positions#

HTMLStates and positions Runnable
<style>
  body { font-family: sans-serif; }
  .btn { padding: 8px 16px; border: 0; border-radius: 6px; background: #2965f1; color: white; cursor: pointer; }
  .btn:hover { background: #1d4ed8; }
  .btn:active { transform: scale(0.97); }
  .btn:focus-visible { outline: 3px solid #f59e0b; outline-offset: 2px; }
  .btn:disabled { background: #94a3b8; cursor: not-allowed; }


  tr:nth-child(even) { background: #f1f5f9; }
  li:first-child { font-weight: bold; }
  li:not(:last-child) { border-bottom: 1px solid #ddd; }


  input:checked + label { color: #16a34a; text-decoration: line-through; }
  .card:has(input:checked) { background: #dcfce7; }
  .card { padding: 6px; border-radius: 6px; }
</style>


<button class="btn">Hover / Tab / Click me</button>
<button class="btn" disabled>Disabled</button>


<table><tr><td>Row 1</td></tr><tr><td>Row 2</td></tr><tr><td>Row 3</td></tr><tr><td>Row 4</td></tr></table>


<ul><li>First item</li><li>Second</li><li>Last</li></ul>


<div class="card"><input type="checkbox" id="t1"><label for="t1">Buy milk</label></div>
Expected output
A button that darkens on hover, shrinks when pressed and shows an orange ring on keyboard focus; a greyed disabled button; striped table rows; a bold first list item; and a to-do that turns green and crossed out when ticked.
Opens this example in the full playground

Step-by-step explanation#

  1. :hover, :active and :focus-visible give the button three interactive states.
  2. :disabled styles the button with the disabled attribute.
  3. tr:nth-child(even) stripes every second row.
  4. li:not(:last-child) adds a divider under every item except the last.
  5. input:checked + label styles the label next to a ticked checkbox.
  6. .card:has(input:checked) styles the parent card when its checkbox is ticked.

Real-world use case#

Buttons, links, form fields, zebra-striped tables, completed to-do items and active menu items are all styled with pseudo-classes.

Common mistakes#

Only styling :hover

Touch screens and keyboards do not hover. Always style :focus-visible too.

Confusing :nth-child and :nth-of-type

p:nth-child(2) means “the 2nd child, if it is a p”. p:nth-of-type(2) means “the 2nd p”.

Wrong link state order

If :hover comes before :visited, visited links never show the hover style. Use LoVe HAte order.

Best practices#

  • Give every interactive element hover, focus-visible and active states
  • Use :disabled and cursor: not-allowed for disabled controls
  • Use :has() for parent styling instead of extra JavaScript
  • Prefer :focus-visible over :focus for outlines

Remember this#

One colon = state (:hover). Two colons = a made-up part (::before).

LoVe HAte: :link → :visited → :hover → :active.

:nth-child(odd) / (even) = instant zebra stripes.

Practice exercise#

Your task: Make every third list item red and give text inputs a blue border when focused.

Need a hint?
:nth-child(3n) and input:focus.
Show solution
CSS Illustrative
li:nth-child(3n) { color: red; }
input[type="text"]:focus { border: 2px solid blue; outline: none; }

Mini quiz#

Pseudo-Classes quizMultiple choice Question 1 of 3

Which selects every odd row of a table?

Score so far: 0/0

Want more? Practise all CSS MCQs · CSS interview questions

My notes#

Saved in this browser.

Progress is saved in this browser. Sign in to sync.