</>
Skip to content
CSS lessons (30/66)

CSS — Pseudo-classes

A colon changes everything

Pseudo-classes style elements by state or position without touching HTML:

a:hover { color: teal; }

Interaction states

button:hover   { background: #0f766e; }   /* mouse over        */
input:focus    { border-color: #0d9488; } /* being typed in    */
button:active  { transform: scale(0.97); }/* being pressed     */
a:visited      { color: #6d28d9; }        /* already opened    */

:hover + :focus-visible + :active on every interactive element is baseline professionalism.

Position among siblings

li:first-child  { font-weight: 700; }
li:last-child   { border-bottom: none; }
li:nth-child(3) { color: red; }        /* exactly the third */

nth-child patterns

li:nth-child(odd)   { background: #f8fafc; }  /* zebra rows! */
li:nth-child(even)  { background: white; }
li:nth-child(3n)    { color: teal; }          /* every third */
li:nth-child(3n+1)  { … }                     /* 1, 4, 7, …  */

The an+b formula: n counts 0,1,2… — covers any rhythm.

Content-state pseudo-classes

/* structural */
:root            { --brand: teal; }       /* the html element */
:empty           { display: none; }       /* no children at all */

/* forms */
input:checked  + span { font-weight: 700; }
input:disabled { opacity: 0.5; cursor: not-allowed; }
input:required { border-left: 3px solid orange; }
input:focus:invalid { border-color: crimson; }

/* links */
a:any-link { text-decoration-thickness: 2px; }

:checked + sibling is the engine behind pure-CSS switches and tabs.

:not — everything except

.menu li:not(:last-child) {
    border-right: 1px solid #ddd;
}

input:not([type="submit"]) {
    border: 1px solid #ccc;
}

p:not(.intro):not(.quote) {
    margin-bottom: 1em;    /* chain for multiple exclusions */
}

Negation keeps CSS honest — style the rule, exclude the exceptions.

:is() — group shorthand

/* instead of repeating: */
header a:hover, main a:hover, footer a:hover { … }

:is(header, main, footer) a:hover {
    color: teal;
}

Cleaner, and specificity uses only the most specific argument.

Specificity note

Most pseudo-classes count like a class (0,1,0). Exceptions worth knowing: :not() itself adds nothing (its argument does), :is() takes its highest argument.

Cheat sheet

NeedSelector
Mouse over:hover
Keyboard/current input:focus / :focus-visible
Every 2nd row:nth-child(even)
All but last:not(:last-child)
Checked checkbox effects:checked + label
Disabled button:disabled
Any of several parents:is(a, b, c)

Mini Practice

  1. Full button state set: hover/focus-visible/active.
  2. Zebra-striped table with nth-child(even).
  3. Pure-CSS toggle: hidden checkbox + :checked + label.
  4. Menu dividers via :not(:last-child) — then add an item and watch it adapt.
  5. Style required vs disabled inputs distinctly.
  6. Collapse three identical rules into one with :is().

Next: pseudo-elements →

Related Topics

Frequently Asked Questions about Pseudo-classes

What is Pseudo-classes in CSS?

Pseudo-classes is a fundamental concept in CSS. This lesson explains it step by step with clear examples, making it easy for beginners to understand.

How do I learn Pseudo-classes?

Start by reading the explanation above, then try the code examples. Practice by modifying the examples and experimenting with different values. Hands-on practice is the best way to learn Pseudo-classes.

Why is Pseudo-classes important in CSS?

Pseudo-classes is essential for CSS development. Understanding this concept will help you write better code and solve real-world problems more effectively.