CSSSelectors Cheat Sheet

CSS Selectors Cheatsheet

Every CSS selector on one page: basic selectors, combinators, all seven attribute operators, the full pseudo-class catalogue (structural, state, and the modern functional ones like :is(), :where(), :has()), pseudo-elements, and how specificity is calculated.

Basic Selectors

Selector

Example

Matches

Universal

*

Every element

Type

p, ul, button

Elements by tag name

Class

.card

Elements with class="card"

ID

#header

The element with id="header"

Compound

button.primary

Elements matching all parts (a button that has the class)

Grouping

h1, h2, h3

Any element matching any listed selector

Combinators

Combinator

Example

Matches

Descendant (space)

article p

Any p inside an article, at any depth

Child >

ul > li

Only direct children

Next sibling +

h2 + p

The first p immediately after an h2

Subsequent siblings ~

h2 ~ p

Every p after an h2 with the same parent

CSS
article p     { line-height: 1.6; }   /* any depth        */
ul > li       { list-style: square; } /* direct child only */
h2 + p        { margin-top: 0; }      /* immediately after */
input ~ label { color: gray; }        /* any later sibling */
Attribute Selectors — All 7 Operators

Syntax

Meaning

Example match

[attr]

Attribute exists

a[target] — any link with a target

[attr="v"]

Exactly equals

input[type="email"]

[attr~="v"]

Contains word v in a space-separated list

[class~="icon"] matches class="btn icon lg"

[attr|="v"]

Exactly v or starts with v-

[lang|="en"] matches en, en-US

[attr^="v"]

Starts with

a[href^="https"]

[attr$="v"]

Ends with

a[href$=".pdf"]

[attr*="v"]

Contains substring

a[href*="example.com"]

CSS
/* Case-insensitive flag */
a[href$=".PDF" i] { color: crimson; }  /* matches .pdf and .PDF */

/* Practical examples */
a[href^="http"]:not([href*="mysite.com"])::after {
  content: " \2197";  /* mark external links */
}
input[type="checkbox"]:checked + label { font-weight: bold; }
Structural Pseudo-classes

Selector

Matches

:first-child / :last-child

First / last element among its siblings

:only-child

An element with no siblings

:nth-child(n)

By position: 2, odd, even, 2n+1, n+3

:nth-last-child(n)

Same, counting from the end

:first-of-type / :last-of-type

First / last sibling of that tag

:nth-of-type(n)

Position counted among same-tag siblings only

:empty

Element with no children (not even text)

:root

The document root — html, but with higher specificity than the type selector

CSS
li:nth-child(odd)      { background: #f6f6f6; } /* zebra rows   */
li:nth-child(3n)       { color: teal; }          /* every third  */
li:nth-child(n + 4)    { display: none; }        /* from 4th on  */
li:nth-last-child(-n + 2) { opacity: 0.5; }      /* last two     */
p:first-of-type        { font-size: 1.2em; }     /* lead paragraph */
State and UI Pseudo-classes

Selector

Matches

:hover / :active

Pointer over / being clicked

:focus

Element has keyboard/programmatic focus

:focus-visible

Focused and the browser decides a ring should show (keyboard)

:focus-within

Element or any descendant is focused

:visited / :link

Visited / unvisited links (styling is restricted for privacy)

:checked

Checked checkbox/radio, selected option

:disabled / :enabled

Form control state

:required / :optional

Form control validation attributes

:valid / :invalid

Form control validity right now

:user-invalid

Invalid after the user interacted — far less aggressive

:placeholder-shown

Input currently showing its placeholder

:target

Element whose id matches the URL fragment

Functional Pseudo-classes: :is(), :where(), :not(), :has()

CSS
/* :is() — collapse repetitive selectors.
   Specificity = the most specific argument. */
:is(h1, h2, h3) a { text-decoration: none; }

/* :where() — identical matching, but specificity is ZERO.
   Perfect for resets and defaults meant to be overridden. */
:where(ul, ol) { margin: 0; padding: 0; }

/* :not() — exclusion. Accepts full selector lists. */
button:not(.primary, .danger) { background: gray; }

/* :has() — the "parent selector". Matches an element
   based on what it CONTAINS. */
.card:has(img)          { padding: 0; }
form:has(:invalid) .submit { opacity: 0.5; }
h2:has(+ p)             { margin-bottom: 0.25em; } /* h2 followed by p */
Note
:where() and :is() match identically — the only difference is specificity. :where() contributes zero, while :is() takes the specificity of its most specific argument, even for the parts that did not match.
Pseudo-elements

Selector

Targets

::before / ::after

Generated content inserted inside the element (needs content)

::first-line / ::first-letter

Typographic fragments of block text

::selection

Text the user has highlighted

::placeholder

An input's placeholder text

::marker

List bullets / numbers

::backdrop

The layer behind a modal dialog or fullscreen element

::file-selector-button

The button inside input type="file"

Tip
Pseudo-elements use double colons (::before); pseudo-classes use one (:hover). Browsers still accept the legacy single-colon form for the four oldest pseudo-elements, but write double colons in new code.
Specificity

Specificity is compared as three numbers (A, B, C) — higher wins, compared left to right. Ties go to the later rule in the stylesheet.

Level

Counts

Examples

A — IDs

Each #id

#nav → (1,0,0)

B — Classes

Classes, attributes, pseudo-classes

.card, [type="text"], :hover → (0,1,0) each

C — Types

Tag names and pseudo-elements

li, ::before → (0,0,1) each

Zero

*, :where(), combinators

Contribute nothing

Special

Inline style="" beats all selectors; !important beats inline

CSS
/* Worked examples */
p                     /* (0,0,1) */
.intro                /* (0,1,0) */
p.intro               /* (0,1,1) */
#main p.intro         /* (1,1,1) */
a:hover               /* (0,1,1) */
:is(#nav, p) a        /* (1,0,1) — :is takes the max argument */
:where(#nav, p) a     /* (0,0,1) — :where is always zero      */
Quick Reference: Selector Performance

Browsers match selectors right to left, so the rightmost part (the key selector) matters most. In practice selector speed is almost never your bottleneck — write for readability, and only worry about extremely broad key selectors (like a bare * or [class*=...]) inside huge documents.