CSS selectors allow you to target specific elements on a web page for styling. While most developers know basics like class, ID, and element selectors, there are some powerful but overlooked selectors that can make CSS a lot cleaner and more efficient. This post will highlight some of the sneaky CSS selectors that you may not be taking advantage of.
The Adjacent Sibling Combinator (+)
The adjacent sibling combinator allows you to target an element that directly follows another specific element. For example:
h2 + p {
/* Styles for paragraphs directly after h2 elements */
color: blue;
}
This is useful for styling elements based on their position in the markup without extra classes or IDs. A common use case is styling the first paragraph after a heading differently than subsequent paragraphs. The adjacent sibling combinator only targets the immediately following sibling element, not any siblings further down.
The General Sibling Combinator (~)
The general sibling combinator targets elements that follow another specified element, not just the directly adjacent element. For example:
h2 ~ p {
/* Styles for all paragraphs following h2 */
font-size: 90%;
}
This allows styling based on elements that are related siblings without regardless of direct adjacency. You can apply styles to any paragraphs coming after an h2
element, even if other elements like images or lists come between them.
The :not() Pseudo-class
The :not()
pseudo-class applies styles to every element except the one(s) specified inside the parentheses. For example:
p:not(.highlight) {
/* Style all paragraphs except ones with class="highlight" */
line-height: 1.5;
}
This provides an easy way to exclude specific elements from styling. Common uses include styling all p
tags except those with a certain class. You can also negate multiple selectors like :not(p, .highlight)
to style any elements that are not p
tags or elements with class highlight
.
The :where() Pseudo-class
The :where()
pseudo-class allows you to combine multiple selectors in one declaration. For example:
:where(p, .highlight) {
/* Styles for both <p> and .highlight elements */
background-color: #ff0;
}
This condenses styling for multiple selectors into one line. It's useful when you want to apply the same styles to different selectors without duplicating property declarations.
The :is() Pseudo-class
Similar to :where()
, :is()
allows styling multiple selectors in one declaration. The key difference is that :is()
does not look within the current element, only direct descendants.
For example:
article:is(aside, section) {
/* Styles direct aside and section descendants of articles */
max-width: 300px;
}
This applies max-width
only to <aside>
and <section>
elements that are direct children of <article>
, rather than all descendants.
The :has() Pseudo-class
The :has()
pseudo-class targets elements that contain a specific descendant element. For example:
article:has(> img) {
/* Styles for article elements containing a direct img descendant */
padding: 1rem;
border: 1px solid #ccc;
}
This allows styling based on nested or child elements in a powerful way. You can uniquely style parents based on the presence or absence of descendants.
Conclusion
CSS has some very useful but overlooked selectors like sibling combinators, negation pseudo-classes, and relationship pseudo-classes. Mastering these sneaky selectors can help write cleaner, more targeted CSS and solve styling problems in clever ways. Selectors like :not()
, :where()
, and :has()
give you precise control over elements without additional markup. The adjacent and general sibling combinators allow styling based on element order and relationships. Don't overlook these sneaky CSS selectors - add them to your toolkit to advance your style abilities.