Pseudo-classes

Learn to select elements based on a state or condition they're in — interaction, form validity, position in the document, and logical combinations — using pseudo-class selectors.

A pseudo-class lets you select an element based on a special STATE or CONDITION, rather than something written explicitly in the HTML like a class or attribute. Is the mouse hovering over this button right now? Is this checkbox checked? Is this the first child of its parent? Pseudo-classes answer questions like these, and they're written with a single colon followed by a keyword, like :hover.

Interaction state pseudo-classes

These respond to how the user is currently interacting with an element:

• :hover — while the pointer is over the element • :focus — while the element has keyboard/input focus (e.g. a focused text field) • :focus-visible — like :focus, but only when the browser determines the focus indicator should be visible (e.g. keyboard navigation, not a mouse click) — great for accessible focus rings without unwanted outlines on mouse clicks • :active — while the element is being actively pressed/clicked • :visited — a link the user has already navigated to (limited styling allowed for privacy reasons — only a few properties like color can be changed)

Form state pseudo-classes

These reflect the state of form controls:

• :checked — a checked checkbox or radio button, or a selected <option> • :disabled — a form control that is disabled and cannot be interacted with • :enabled — a form control that CAN be interacted with (the default state) • :required — an input with the required attribute • :valid — an input whose current value passes its validation constraints • :invalid — an input whose current value fails its validation constraints

Structural pseudo-classes

These select elements based on their position among siblings in the document tree:

• :first-child — an element that is the first child of its parent • :last-child — an element that is the last child of its parent • :nth-child(n) — an element at a specific position, using a number, a keyword (odd/even), or a formula like 3n+1

Logical (functional) pseudo-classes

These take other selectors as arguments and combine them logically:

• :not(selector) — matches an element that does NOT match the given selector • :is(selector-list) — matches an element that matches ANY selector in the list (useful for shortening groups of selectors) • :where(selector-list) — like :is(), but always contributes zero specificity, making it useful for easily-overridable base styles • :has(selector) — matches an element that CONTAINS a match for the given selector (a genuine 'parent selector'); this is a newer feature, so always check current browser support before relying on it in production

Pseudo-class syntax

selector:pseudo-class {
    property: value;
}

/* functional pseudo-classes take arguments */
selector:nth-child(2n) { }
selector:not(.excluded) { }
selector:is(h1, h2, h3) { }
selector:has(> img) { }

Code Examples

Applies only while the pointer is hovering over a <button> — the background reverts once the pointer leaves.:focus-visible shows a clear outline for keyboard users navigating with Tab, without necessarily showing it on a plain mouse click.:disabled visually communicates a form field can't be used.

Creates alternating 'zebra stripe' row backgrounds on a list using:nth-child(odd), and makes the very first item bold using:first-child.:not() can be chained to exclude multiple cases.:has() lets figure styling depend on whether it contains an <img> — effectively a 'parent selector'. Remember::has() is newer, so verify browser support (it's well-supported in modern evergreen browsers, but check if you must support older ones).

button:hover {
    background-color: darkslateblue;
}
input:focus-visible {
    outline: 3px solid dodgerblue;
}

input:disabled {
    background-color: #eee;
    cursor: not-allowed;
}
li:nth-child(odd) {
    background-color: #f7f7f7;
}

li:first-child {
    font-weight: bold;
}
/* Style any input that is NOT a checkbox or radio */
input:not([type="checkbox"]):not([type="radio"]) {
    border: 1px solid #ccc;
    padding: 8px;
}

/* Style a <figure> that contains an image, using :has() */
figure:has(img) {
    border: 1px solid #ddd;
}

Pseudo-class categories

Grouping pseudo-classes this way helps you remember which 'question' each one is answering about an element.

Interaction:   :hover  :focus  :focus-visible  :active  :visited
Form state:    :checked  :disabled  :enabled  :required  :valid  :invalid
Structural:    :first-child  :last-child  :nth-child(n)
Logical:       :not()  :is()  :where()  :has()

Important Rules

Pseudo-classes use a single colon: :hover, not ::hover.

They select real elements based on state or position, not fake sub-elements.:focus-visible is generally preferred over:focus for accessible-but-unobtrusive focus outlines.:where() has zero specificity, making its styles trivially easy to override;:is() keeps normal specificity.:has() is powerful but newer — always verify browser support before depending on it in production.

Accessibility and pseudo-classes

:focus-visible is now the standard recommendation for keyboard-accessible focus indicators, replacing the older habit of either always showing :focus outlines (which some found visually noisy on mouse clicks) or removing them entirely with outline: none (which badly hurts keyboard-only and screen-reader users).

a:visited { background-image: url("visited-icon.png"); } a:visited { color: purple; }:visited is deliberately restricted by browsers to a small set of properties (mainly color-related) for user privacy reasons — you cannot use it to reveal browsing history through things like background images or layout changes.

[Concept Matching] Which pseudo-class would correctly select a submit button ONLY while the user is actively pressing it down (not merely hovering, and not after release)? A. :hover B. :focus C. :active D. :visited

C. :active — :active applies only during the actual press/click action. :hover applies whenever the pointer is over the element (before, during, and after a click), and :focus applies when the element has input focus, which can persist after the click ends.

Write CSS using :nth-child() to give every third item in a list a light blue background.

li:nth-child(3n) { background-color: lightblue; } nth-child accepts a formula like 3n.