Menu

CSS Pseudo-Classes: nth-child, not, first-child, focus

Pseudo-classes select elements by position or state: :nth-child(), :first-child, :not(), :focus, :checked, :has() and more. Learn the syntax, the an+b formula with a live tester, and the difference between nth-child and nth-of-type.

This page includes runnable editors - edit, run, and see output instantly.

What a pseudo-class is

A pseudo-class is a keyword after a colon that selects elements by their position in the document or their state. You attach it to a normal selector: li:first-child, input:focus, a:hover.

State pseudo-classes for links and the mouse (:hover, :active, :visited) have their own page, CSS hover.

:nth-child() and the an+b formula

:nth-child() takes a number, a keyword, or a formula an+b. The browser tries n = 0, 1, 2, ... and matches every position the formula produces (positions start at 1).

FormulaMatches positionsMeaning
33The third child
odd or 2n+11, 3, 5, ...Odd rows
even or 2n2, 4, 6, ...Even rows
3n3, 6, 9, ...Every third
n+44, 5, 6, ...The fourth and after
-n+31, 2, 3The first three
3n+11, 4, 7, ...Every third, starting at the first

Type a formula and watch which items match:

Try odd, even, 4, n+7, -n+5 and 2n+3. An invalid formula matches nothing.

The last-counting twin is :nth-last-child(): li:nth-last-child(-n+2) selects the last two items.

nth-child vs nth-of-type

:nth-child() counts every sibling element, whatever its tag, and only then checks the rest of the selector. :nth-of-type() counts only siblings of the same tag. The difference shows as soon as the siblings are mixed:

The same pairs exist for the ends: :first-child / :first-of-type, :last-child / :last-of-type, :only-child / :only-of-type.

:not(), :is() and :where()

Pseudo-classExampleMatches
:not(X)li:not(.done)Elements that do not match X
:is(X, Y):is(h1, h2, h3) aElements matching any of the list
:where(X, Y):where(ul, ol) liSame as :is(), with zero specificity
:has(X)li:has(> input:checked)Elements containing a match for X

:not() is the one used every day, for example to put a separator between items but not after the last one:

State pseudo-classes: focus, checked, disabled, invalid

Pseudo-classMatches
:focusThe element that has keyboard or mouse focus
:focus-visibleFocused, and the browser decides a ring should show (usually keyboard focus)
:focus-withinAn element that contains the focused element
:checkedA checked checkbox, radio or selected <option>
:disabled / :enabledForm controls that are (not) disabled
:required / :optionalInputs with or without required
:invalid / :validInputs whose value fails or passes validation
:user-invalidInvalid, but only after the user has interacted with it
:placeholder-shownAn input currently showing its placeholder
:emptyAn element with no children, not even text
:targetThe element whose id matches the URL's #fragment

Click into the email field: its label turns blue through :focus-within. Type something that is not an email and leave the field: :user-invalid turns the border amber. Plain :invalid would have marked the empty required field amber before you touched it.

Whatever you do with :focus, keep a visible focus style. Removing the outline without a replacement leaves keyboard users unable to see where they are.

:has(), the parent selector

:has() matches an element by what it contains. It is the long-requested "parent selector", and it works in current Chrome, Firefox and Safari.

Pick the other plan: the whole card follows the radio button, with no JavaScript.

Common mistakes

  • A space before the colon. li :first-child means "a first child inside an li". Write li:first-child.
  • Expecting p:nth-child(1) to mean "the first p". It means "the first child, if it is a p". Use :first-of-type for the first p.
  • Counting from 0. Positions start at 1: :nth-child(0) matches nothing.
  • Removing focus outlines. :focus { outline: none } with no replacement breaks keyboard navigation.
  • Confusing : and ::. :before is an old spelling of the pseudo-element ::before; pseudo-classes always use one colon.

Frequently Asked Questions

What is a pseudo-class in CSS?

A keyword after a colon that selects elements by state or position rather than by name, such as a:hover, li:first-child or input:focus. It is added to a normal selector, and most pseudo-classes count like a class for specificity (:where() counts zero).

How does nth-child work in CSS?

:nth-child(an+b) matches elements whose position among their siblings is a×n+b for n = 0, 1, 2 and so on, counting from 1. :nth-child(3) is the third child, :nth-child(odd) or 2n+1 every odd one, and :nth-child(n+4) the fourth and after.

What is the difference between nth-child and nth-of-type?

:nth-child() counts all sibling elements, then checks the rest of the selector; :nth-of-type() counts only siblings of the same element type. In a list of an h2 followed by paragraphs, p:nth-child(1) matches nothing while p:nth-of-type(1) matches the first paragraph.

How do you use :not() in CSS?

Put the selector to exclude inside the parentheses: li:not(.done) matches every list item without the class done. It accepts a list too, input:not([type="checkbox"], [type="radio"]).

What is the difference between a pseudo-class and a pseudo-element?

A pseudo-class (one colon, :hover) selects a whole existing element in some state. A pseudo-element (two colons, ::before) targets a part of an element or creates generated content.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED