CSS Selectors Cheat Sheet
Quick reference for CSS selectors: basic, attribute, pseudo-classes, pseudo-elements, and combinators.
CSS selectors are patterns that target HTML elements for styling. The CSS Selectors Level 3 / 4 specifications (see W3C Selectors Level 3 and the MDN reference) define everything below. Modern browsers support all of these; older IE-specific selectors (*+*, :-ms-*) are omitted.
Basic selectors
| Selector | Meaning | Example |
|---|---|---|
* | Universal — any element | * { box-sizing: border-box; } |
div | Type selector — by tag name | h1 { color: teal; } |
.cls | Class | .btn { padding: .5rem 1rem; } |
#id | ID (one per document) | #app { margin: 0; } |
A, B | Selector list — match any of the selectors | h1, h2, h3 { font-family: serif; } |
Multiple classes stack: .btn.primary matches an element with both classes.
Combinators
Combinators describe the relationship between two simple selectors.
| Combinator | Name | Meaning | Example |
|---|---|---|---|
A B | Descendant | B anywhere inside A | article p |
A > B | Child | B is a direct child of A | ul > li |
A + B | Adjacent sibling | B immediately follows A as a sibling | h1 + p |
A ~ B | General sibling | Any B sibling that follows A | h1 ~ p |
/* only direct children */
.nav > li { padding: 0 .5rem; }
/* first paragraph after each h2 */
h2 + p { margin-top: 0; }
/* every sibling paragraph after an h1 */
h1 ~ p { font-size: .95rem; }
Attribute selectors
| Selector | Meaning |
|---|---|
[attr] | Element has the attribute (any value) |
[attr="v"] | Value equals v (exact, case-sensitive) |
[attr~="v"] | Value is a whitespace-separated word list containing v |
| `[attr | ="v"]` |
[attr^="v"] | Value starts with v |
[attr$="v"] | Value ends with v |
[attr*="v"] | Value contains v |
[attr="v" i] | Case-insensitive match (add s for case-sensitive default) |
input[type="email"] { /* ... */ }
a[href^="https://"] { /* ... */ }
img[src$=".svg"] { /* ... */ }
a[href*="example"] { /* ... */ }
[data-theme="dark" i] { /* ... */ }
Pseudo-classes
A pseudo-class targets a state or structural position. Pseudo-classes are written with a single colon (:).
| Pseudo-class | Selects |
|---|---|
:hover | Element under the pointer |
:focus | Element has keyboard focus |
:focus-visible | Element has keyboard focus that the UA determines is appropriate |
:active | Element being activated (e.g. mouse-down) |
:visited / :link | Visited / unvisited <a> / <area> links |
:first-child | First child of its parent |
:last-child | Last child of its parent |
:only-child | Sole child of its parent |
:nth-child(n) | Nth child (1-indexed; 2n, odd, even) |
:nth-last-child(n) | Nth child counting from the end |
:first-of-type | First sibling of its type |
:last-of-type | Last sibling of its type |
:nth-of-type(n) | Nth sibling of its type |
:only-of-type | Sole sibling of its type |
:not(s) | Negation — elements not matched by selector s |
:is(s) | Matches if any selector in s matches |
:where(s) | Like :is but contributes 0 specificity |
:has(s) | Matches if the element contains a descendant matching s |
:root | The document root (<html>) |
:empty | Element with no children (including text nodes) |
:checked | Checked checkbox / radio |
:disabled / :enabled | Form element state |
:required / :optional | Form validation attribute |
:valid / :invalid | Element meets / fails its constraints |
:target | Element whose id matches the URL fragment |
:lang(en) | Element with language en |
/* every other row in a table */
tr:nth-child(2n) { background: #f4f4f4; }
/* any direct child that is not a span */
li:not(span) { /* ... */ }
/* card containing an image */
.card:has(img) { grid-template-rows: auto 1fr; }
Pseudo-elements
Pseudo-elements style a specific part of an element. They are written with two colons (::); single-colon legacy aliases (:before, :after) still work.
| Pseudo-element | Targets |
|---|---|
::before | Generated content before the element's content |
::after | Generated content after the element's content |
::first-line | First formatted line of a block |
::first-letter | First letter of the first formatted line |
::placeholder | Placeholder text of an input |
::selection | The portion currently highlighted by the user |
::marker | The bullet or number of a list item |
.note::before { content: "ℹ "; color: teal; }
input::placeholder { color: #999; }
Specificity
Specificity decides which rule wins when two selectors match the same element. It is computed as a 4-tuple (inline, id, class/attr/pseudo-class, element/pseudo-element). Higher components dominate lower ones.
| Component | Example | Weight |
|---|---|---|
Inline style="..." | <div style="..."> | 1,0,0,0 |
| ID selector | #header | 0,1,0,0 |
| Class, attribute, pseudo-class | .btn, [type="text"], :hover | 0,0,1,0 |
| Element, pseudo-element | h1, ::before | 0,0,0,1 |
| Universal selector | * | 0,0,0,0 |
Quick calculation:
#nav .item.active a:hover /* id (1) class (3) element (1) -> (0,1,3,1) */
ul#nav li.active /* id (1) class (1) element (2) -> (0,1,1,2) */
The first rule wins on specificity. Tied selectors defer to source order.
:is() and :where() and specificity
:is(a, b, c) matches any of its arguments, and its specificity is the highest specificity of its arguments. :where(...) does the same but contributes zero specificity — useful for portable selectors in libraries.
:is(#main, .sidebar) p /* specificity 1,0,0,1 — like #main p */
:where(#main, .sidebar) p /* specificity 0,0,0,1 — like p */
!important overrides specificity but is reserved for last-resort cases (utility classes, accessibility fixes).