CSS Selectors Cheatsheet
Every selector type with examples — from basics to :has()
Basic Selectors
| Selector | Example | Selects |
|---|---|---|
| Element | p | All <p> elements |
| Class | .card | Elements with class="card" |
| ID | #header | Element with id="header" |
| Universal | * | All elements |
| Attribute | [type="email"] | Elements with type="email" |
Combinators
| Combinator | Example | Selects |
|---|---|---|
| Descendant (space) | .nav a | Any <a> inside .nav (any depth) |
| Child (>) | .nav > a | Direct <a> children of .nav only |
| Adjacent sibling (+) | h2 + p | First <p> immediately after <h2> |
| General sibling (~) | h2 ~ p | All <p> after <h2> (same parent) |
/* Only direct children */
.menu > li { list-style: none; }
/* First paragraph after a heading */
h2 + p { font-size: 1.1rem; margin-top: 8px; }
Pseudo-Classes
User Action
a:hover { color: #4f46e5; } /* mouse over */
a:active { color: #3730a3; } /* being clicked */
a:focus { outline: 2px solid; } /* keyboard focus */
a:visited { color: #7c3aed; } /* previously visited */
input:focus-visible { outline: 2px solid #6366f1; } /* keyboard focus only */
Structural
li:first-child { font-weight: bold; }
li:last-child { border-bottom: none; }
li:nth-child(odd) { background: #f8fafc; }
li:nth-child(3n) { color: red; } /* every 3rd */
li:nth-child(n+4) { opacity: 0.6; } /* 4th and beyond */
tr:nth-of-type(even) { background: #f1f5f9; }
p:only-child { margin: 0; }
div:empty { display: none; }
Form States
input:required { border-left: 3px solid #6366f1; }
input:disabled { opacity: 0.5; cursor: not-allowed; }
input:checked { accent-color: #6366f1; }
input:valid { border-color: #10b981; }
input:invalid { border-color: #ef4444; }
input:placeholder-shown { background: #f8fafc; }
Negation and Matching
/* Everything except .special */
.item:not(.special) { opacity: 0.8; }
/* Matches any of these selectors */
:is(h1, h2, h3) { font-weight: 700; }
/* Same as :is() but with zero specificity */
:where(h1, h2, h3) { margin-top: 1em; }
/* Select multiple nth-children at once */
:nth-child(2 of .highlight) { background: yellow; }
The :has() Selector (Parent Selector)
The most powerful selector addition in years. It selects elements based on their descendants:
/* Card that contains an image */
.card:has(img) {
padding: 0;
}
/* Form group with an invalid input */
.form-group:has(input:invalid) {
border-color: #ef4444;
}
/* h2 followed by a paragraph (previous-sibling selector!) */
h2:has(+ p) {
margin-bottom: 8px;
}
/* Page that has a modal open */
body:has(.modal.open) {
overflow: hidden;
}
Supported in Chrome 105+, Safari 15.4+, Firefox 121+. Safe to use in production.
Pseudo-Elements
/* Insert content before/after */
.required::before { content: "*"; color: red; }
blockquote::before { content: "\201C"; font-size: 2rem; } /* opening quote */
/* Style the first line or letter */
p::first-line { font-weight: 600; }
p::first-letter { font-size: 2rem; float: left; }
/* Style text selection */
::selection { background: #6366f1; color: white; }
/* Style placeholder text */
input::placeholder { color: #94a3b8; }
/* Style scrollbar (webkit) */
::-webkit-scrollbar { width: 8px; }
::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 4px; }
Attribute Selectors
| Selector | Matches |
|---|---|
[href] | Has the attribute (any value) |
[type="submit"] | Exact value match |
[class~="card"] | Word in space-separated list |
[href^="https"] | Starts with "https" |
[src$=".png"] | Ends with ".png" |
[data-type*="nav"] | Contains "nav" anywhere |
[lang|="en"] | Exactly "en" or starts with "en-" |
/* External links */
a[href^="http"]:not([href*="zerokit.dev"]) {
&::after { content: " ↗"; }
}
/* File type icons */
a[href$=".pdf"]::before { content: "📄 "; }
a[href$=".zip"]::before { content: "📦 "; }
Specificity
Specificity determines which rule wins when multiple rules target the same element. It is calculated as (IDs, Classes, Elements):
| Selector | Specificity |
|---|---|
p | (0, 0, 1) |
.card | (0, 1, 0) |
#header | (1, 0, 0) |
.card .title | (0, 2, 0) |
#nav .link:hover | (1, 2, 0) |
div.card > p.text | (0, 2, 2) |
:where() has zero specificity — useful for defaults that are easy to override. :is() takes the specificity of its most specific argument.
Frequently Asked Questions
How does CSS specificity work?
Specificity is a three-part score: (IDs, Classes, Elements).
#nav = (1,0,0). .active = (0,1,0). div = (0,0,1). Higher scores win. Equal scores: last rule in source order wins. !important beats everything but should be avoided. :where() has zero specificity.What is the difference between :nth-child and :nth-of-type?
:nth-child(n) counts among ALL siblings. :nth-of-type(n) counts among siblings of the same element type. p:nth-child(2) matches a <p> only if it is the 2nd child overall. p:nth-of-type(2) matches the 2nd <p> regardless of other elements.What does :has() do?
:has() is a parent selector — it selects an element based on its contents. .card:has(img) selects cards with images. h2:has(+ p) selects h2 followed by p (previous-sibling selector). Supported in all major browsers.