CSS Selectors
CSS selectors are the targeting system of the web. They instruct the browser rendering engine exactly which DOM nodes to select and style. As layout complexity scales, writing clean, maintainable selectors becomes essential for preventing specificity bloat and redundant rule chains.
1. Core Selector Taxonomyβ
CSS selectors range from fundamental type matchers to advanced functional pseudo-classes:
[ CSS Selectors ]
|
βββββββββββββββββββ¬βββββββ΄ββββββββ¬βββββββββββββββββββ
βΌ βΌ βΌ βΌ
Simple Attribute Combinators Pseudo-Classes /
Selectors Selectors (& Relational) Elements
(type, class, ([attr=val]) (>, +, ~) (:hover, ::before,
id, universal) :is(), :has())
Basic & Combinator Referenceβ
| Type | Syntax Example | Description | Specificity Vector |
|---|---|---|---|
| Universal | * | Targets every element in the document scope | (0, 0, 0) |
| Type (Element) | h1, p, article | Matches nodes by HTML tag name | (0, 0, 1) |
| Class | .card, .btn | Matches nodes with a specific class attribute value | (0, 1, 0) |
| ID | #main-header | Matches the unique element with a specific id attribute | (1, 0, 0) |
| Descendant | article p | Matches p anywhere nested inside article | (0, 0, 2) |
| Child | ul > li | Matches li that is a direct child of ul | (0, 0, 2) |
| Next Sibling | h2 + p | Matches p immediately following h2 at the same DOM level | (0, 0, 2) |
| Subsequent Sibling | h2 ~ p | Matches all p elements following h2 at the same DOM level | (0, 0, 2) |
2. Advanced Attribute Matchingβ
Attribute selectors target elements based on the presence, exact value, or partial matching of their HTML attributes.
/* 1. Exact match */
input[type="text"] { border-color: #cbd5e1; }
/* 2. Contains word in space-separated list (~=) */
[class~="primary"] { font-weight: 700; }
/* 3. Starts with prefix, followed by hyphen or exact (-=) */
[lang|="en"] { quotes: "β" "β"; }
/* 4. Starts with exact substring (^=) */
a[href^="https://"] { color: #059669; }
/* 5. Ends with exact substring ($=) */
a[href$=".pdf"] { padding-right: 1.5rem; }
/* 6. Contains substring anywhere (*=) */
[class*="nav-item"] { opacity: 0.9; }
/* 7. Case-insensitive matching flag (i) */
input[placeholder*="search" i] { background-color: #f1f5f9; }
3. Modern Pseudo-Classes: :is(), :where(), and :has()β
Modern CSS introduces functional pseudo-classes that dramatically streamline complex rule sets and introduce relational selector logic.
:is() β Grouping & Forgiving Listsβ
Reduces repetition when applying styles across multiple matching trees. The specificity of :is() equals the highest-specificity selector in its argument list.
/* Legacy repetitive approach */
header h1, header h2, main h1, main h2 {
margin-bottom: 1rem;
}
/* Modern clean approach with :is() */
:is(header, main) :is(h1, h2) {
margin-bottom: 1rem;
}
:where() β Zero-Specificity Abstractionβ
Functions identically to :is(), but always maintains a specificity vector of (0, 0, 0), making it ideal for base resets and design systems that users can easily override.
/* Specificity is strictly (0, 0, 0) regardless of internal arguments */
:where(header, main) :where(h1, h2) {
color: #1e293b;
}
/* Easily overridden by a single type selector (0, 0, 1) */
h1 {
color: #2563eb;
}
:has() β The Parent & Relational Selectorβ
Matches a parent element based on its children or sibling relationships.
/* Target a .card parent ONLY if it contains an image */
.card:has(img) {
grid-template-columns: 120px 1fr;
}
/* Target an h2 ONLY when directly followed by a sub-heading p */
h2:has(+ p.subtitle) {
margin-bottom: 0.25rem;
}
Interactive Playground: Selector Precisionβ
Experiment with basic combinators, attribute matching, and functional pseudo-classes in the sandbox below:
Summary Reference Tableβ
| Selector / Pseudo-Class | Primary Use Case | Specificity Impact |
|---|---|---|
[attr^="val"] | Matches starting value (e.g., protocol links) | Counts as 1 Class (0, 1, 0) |
[attr$="val"] | Matches ending value (e.g., file extensions) | Counts as 1 Class (0, 1, 0) |
:is(A, B) | Dry up duplicate selector trees | Highest argument in list |
:where(A, B) | Low-specificity design system defaults | Always zero (0, 0, 0) |
:has(Selector) | Relational parent/sibling conditional styling | Highest argument in condition |