Selectors & Specificity
Use advanced CSS selectors to style elements with precision and less markup
What you'll learn
Use attribute selectors for data-driven styling
Apply pseudo-classes: :nth-child, :has, :not, :is, :where
Use pseudo-elements ::before, ::after, ::first-letter for decoration
Combine selectors with combinators (space, >, +, ~)
Use :has() as a parent selector
Understand specificity differences between :is() and :where()
Write nth-child formulas like 2n+1, -n+3
Attribute Selectors
Target elements by their attributes and values.
/* Exact match */
input[type="email"] {
border-color: #6366f1;
}
/* Starts with */
a[href^="https"] {
color: green;
} /* secure links */
a[href^="mailto"] {
color: orange;
} /* email links */
/* Contains */
img[alt*="screenshot"] {
outline: 2px solid blue;
}
/* Ends with */
a[href$=".pdf"]::after {
content: " (PDF)";
}
/* Data attributes */
[data-state="active"] {
background: #6366f1;
color: white;
}
Pseudo-Classes
Pseudo-classes describe a state or position.
/* Structural */
li:nth-child(2n + 1) {
background: #f8fafc;
} /* odd rows */
li:nth-child(-n + 3) {
font-weight: bold;
} /* first 3 items */
li:last-child {
border-bottom: none;
}
/* Negation */
input:not(:focus):not(:placeholder-shown) {
border-color: #ef4444;
}
/* Grouping with :is() : lower specificity than writing each */
:is(h1, h2, h3) {
margin-top: 1.5em;
}
:is(.card, .alert) > p {
margin-bottom: 0;
}
/* Zero-specificity :where() */
:where(.card, .alert) > p {
margin-bottom: 0;
} /* 0-0-0, easy to override */
:is() vs :where() Specificity
:is() takes the specificity of its most specific argument. :where() always has zero specificity, making it ideal for resets and base styles you want to override easily.
Pseudo-Elements
Pseudo-elements create virtual elements for decoration.
p::first-letter {
font-size: 3em;
float: left;
color: #6366f1;
margin-right: 0.1em;
}
.tooltip {
position: relative;
}
.tooltip::after {
content: attr(data-tip);
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
background: #1e293b;
color: white;
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-size: 0.75rem;
white-space: nowrap;
}
Combinators
/* Descendant (space): any child depth */
.menu a {
color: inherit;
}
/* Child (>): direct children only */
.menu > li {
border-bottom: 1px solid #e2e8f0;
}
/* Adjacent sibling (+): immediately after */
h2 + p {
margin-top: 0;
} /* first p after each h2 */
/* General sibling (~): any later sibling */
.error ~ .error-message {
display: block;
}
:has() — The Parent Selector
:has() selects an element based on its descendants.
/* Select card that contains an image */
.card:has(img) {
grid-column: span 2;
}
/* Style form group with invalid input */
.form-group:has(input:invalid) label {
color: #ef4444;
}
/* Highlight a list item containing 'active' */
li:has(> .active) {
background: #f0fdf4;
}
/* Style sidebar when scrolled */
.sidebar:has(p:hover) {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
nth-child Formulas
an + b — every a elements starting from offset b.
tr:nth-child(odd) {
background: #f8fafc;
} /* 1, 3, 5, ... (2n+1) */
tr:nth-child(even) {
background: white;
} /* 2, 4, 6, ... (2n) */
li:nth-child(3n + 1) {
color: #6366f1;
} /* 1, 4, 7, ... */
li:nth-child(-n + 3) {
font-size: 1.1em;
} /* first 3 items */
li:nth-child(n + 4):nth-child(-n + 6) {
} /* items 4-6 */
Style a Complex List
Code
html
1<ul class="task-list">2<li>Task one - normal</li>3<li>Important - do this first</li>4<li>Task three - normal</li>5<li>Task four - normal</li>6<li>Important - deadline today</li>7<li>Task six - normal</li>8</ul>A task list styled with `:nth-child` selectors to target alternating items, the first 3 items, and specific ranges. The preview highlights which list items each selector matches.
Key Takeaways
Attribute selectors style elements by [attr], [attr=value], [attr^=], [attr$=], [attr*=]
Pseudo-classes describe element state or position in document tree
::before and ::after create virtual elements for content decoration
Combinators: space (descendant), > (child), + (adjacent sibling), ~ (general sibling)
:has() selects parent elements based on their children
nth-child uses formula an+b for pattern-based selection