Cheatsheet

CSS Selectors Cheatsheet

# CSS Selectors Cheatsheet

Selectors decide which elements a rule targets; specificity decides which rule wins when two selectors both match the same element. Most "my CSS isn't applying" bugs are a specificity or source-order problem, not a syntax problem — this sheet covers both.

Quick reference

Basic selectors

Selector Matches Example
`*` Every element `* { box-sizing: border-box; }`
`tag` All elements of that tag `p { }`
`.class` Elements with that class `.card { }`
`#id` The element with that id `#header { }`
`[attr]` Elements with the attribute present `[disabled] { }`
`[attr="val"]` Exact attribute value `[type="submit"] { }`
`[attr~="val"]` Attribute is a space-separated list containing `val` `[class~="active"]`
`[attr^="val"]` Attribute value starts with `val` `[href^="https"]`
`[attr$="val"]` Attribute value ends with `val` `[href$=".pdf"]`
`[attr*="val"]` Attribute value contains `val` `[class*="col-"]`

Combinators

Combinator Meaning Example
`A B` (space) B is any descendant of A `nav a`
`A > B` B is a direct child of A `ul > li`
`A + B` B immediately follows A (same parent) `h2 + p`
`A ~ B` B is any later sibling of A (same parent) `h2 ~ p`

Common pseudo-classes

Selector Matches
`:hover` / `:focus` / `:active` Interaction states
`:first-child` / `:last-child` Position among siblings, all sibling types count
`:nth-child(n)` nth position; also accepts `2n`, `2n+1`, `odd`, `even`
`:not(sel)` Excludes elements matching `sel`
`:is(a, b, c)` Matches if any of the list matches — takes the *highest* specificity in the list
`:where(a, b, c)` Same matching as `:is()` but **always contributes zero specificity**
`:has(sel)` Matches an element if it contains something matching `sel` (a real "parent selector")
`:checked` / `:disabled` Form element state

Pseudo-elements

Selector Targets
`::before` / `::after` Generated content (requires a `content` property)
`::first-line` / `::first-letter` First line/letter of block text
`::placeholder` An input's placeholder text
`::selection` User-highlighted text

Common patterns

Specificity, calculated

Specificity is a 3-part tuple, compared left to right — it is not simple addition:

Selector type Weight
Inline `style="..."` Beats every selector below
ID selectors (`#id`) (1, 0, 0) per ID
Class, attribute, pseudo-class (`.class`, `[attr]`, `:hover`) (0, 1, 0) each
Type selectors, pseudo-elements (`div`, `::before`) (0, 0, 1) each
Universal `*`, combinators 0 — no specificity contribution
#nav .item        /* (1,1,0) */
.nav .item.active /* (0,3,0) */
nav ul li a       /* (0,0,4) */

(1,1,0) beats both of the others — one ID outweighs any number of classes, and any number of classes outweighs any number of type selectors, regardless of total count.

Target every child except the last

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

Style a label only when its input is checked

input:checked + label {
  font-weight: bold;
}

Style a card only if it contains an image (real parent selector)

.card:has(img) {
  padding-top: 0;
}

Pitfalls

  • !important beats specificity entirely, then specificity breaks ties among !important rules: reaching for !important to win a cascade fight usually means the real fix is lowering some other rule's specificity — it doesn't scale, since the next override needs !important too.
  • Two equally specific rules: the one later in source order wins, not the more "specific-looking" one. A .button { color: red; } defined after .button { color: blue; } in the same stylesheet always wins, regardless of which rule "seems" more important.
  • :is() takes on the specificity of its most specific argument; :where() always contributes zero: :is(#id, .class) has ID-level specificity because of #id inside it — a frequent surprise when people assume :is()/:where() are specificity-neutral wrappers.
  • nth-child counts ALL sibling types, not just same-tag siblings: p:nth-child(2) only matches if the actual 2nd child of the parent happens to be a <p> — it does not mean "the 2nd <p>." Use :nth-of-type() when you want to count only same-tag siblings.
  • Attribute selectors are case-sensitive by default for the value (except in HTML's boolean/enumerated attributes) — [href$=".PDF"] will not match .pdf. Add i before the closing bracket for a case-insensitive match: [href$=".pdf" i].

Related ZipKit tools

  • Color Picker — pick and convert the exact color values you're targeting with attribute or class selectors.
  • Box Shadow Builder — generate box-shadow values to pair with :hover/:focus state selectors.
  • SVG Path Editor & Visualizer — style inline SVG elements that ::before/::after can't reach directly.

Related cheatsheets

  • Regex Cheatsheet — the pattern-matching syntax behind attribute selectors like [href^="val"].
  • Markdown Syntax Cheatsheet — another plain-text syntax reference for the same "scannable quick lookup" use case.