CSS pseudoselectors become very helpful when you can’t touch a site’s underlying PHP or markup.
Today’s article comes from my experience working with small business WordPress clients. These clients often come to you with a huge mass of PHP code that you can’t change. For example, they’re trying to combine a social sharing plugin and a page builder theme, and it’s almost working but they can’t manage to fix one or two display details. “Throw everything out” is not an option, and neither is attempting to wrangle with either the theme’s or the plugin’s spaghetti code.
These types of projects make the purist in me shudder, but since good clients with real needs often find themselves with these types of setups, it’s certainly best to know how to help them. CSS pseudoselectors are an area of web development that becomes very helpful when you can’t touch the underlying PHP—so today, let’s learn a bit of CSS dark magic.
Intro to Pseudoselectors
CSS pseudoselectors significantly expand the power of CSS to intelligently select specific elements on a webpage, and to make types of changes that wouldn’t be possible otherwise.
Pseudo Classes and Pseudo Elements
I use “CSS pseudoselectors” as a general term for anything in CSS that looks like this: :selector. These “things” come in two flavors: pseudo classes and pseudo elements.
Pseudo Classes
Pseudo classes select elements on the page based on things that are true about those elements. Here are two very well-known examples:
a:visited {
color: purple;
}Code language: CSS (css)
This will turn any previously visited link purple. The previously visited bit, :visited, is the pseudo class.
li:first-child {
background-color: #aaa;
}Code language: CSS (css)
This will set a light gray background for any li element that is the first among its siblings—in other words, the :first-child of its parent (which should be an ol or ul element).
Pseudo Elements
Pseudo elements are a bit more complex, and quite powerful: They actually let you create content on a webpage using CSS, which isn’t otherwise possible. The most common example is :after:
a:after {
content: " click me!"
font-size: .8em;
}Code language: CSS (css)
This puts a small “click me!” text directly after every link on your site—which I don’t recommend. :after has a fraternal twin, :before, which functions similarly but places content before instead of after the selected element.
These pseudo elements have a lot of uses, so they’re well worth understanding.
📚 Read Up! If this article is news to you so far, I recommend checking out MDN’s documentation on pseudo-classes and pseudo-elements. Even if you understand pseudoselectors, it’s worth browsing the full list—there are quite a few you might not know!
Fancier CSS Pseudoselectors: :not, :nth-child, and Beyond
Modern CSS introduced some very helpful pseudo classes, such as :not and :nth-child:
:not(p) {
color: #000;
}
Code language: CSS (css)
This turns the text black for anything that is not a paragraph (p element).
li:nth-child(3) {
display: none;
}
Code language: CSS (css)
This hides and removes from the page’s layout flow any li that is the third child of its parent.
These pseudo classes are now universally supported across all modern browsers, including mobile. We can safely use them without worrying about older browsers.
Chaining CSS Pseudoselectors
We can chain CSS pseudoselectors to make fairly intricate changes.
This is what lets us solve lots of problems that we’d normally need either JavaScript or access to a site’s PHP to address.
Here’s a simple example using WPShout’s old nav menu. The example’s silly, but it mirrors real client requests. Let’s say I thought the following changes to WPShout’s nav menu would bring a lot more traffic:

Here’s the thing, though: in this pretend example, WPShout’s nav menu is generated by a badly written theme with buried template files—so editing the PHP is far more trouble than it’s worth. All we’ve got is the markup that the theme generates, and the ability to edit the site’s CSS.
What We’ve Got to Work With: Menu Markup
Here’s a simplified version of the markup we see on the front end (using Chrome’s Developer Tools or similar):
<ul>
<li class="nav-item">
<a href="https://wpshout.com/articles/">Articles</a>
</li>
<li class="nav-item">
<a href="https://wpshout.com/links/">Links</a>
</li>
<li class="nav-item">
<a href="https://wpshout.com/about/">About</a>
</li>
<li class="nav-item">
<a href="https://wpshout.com/mailing-list/">Newsletter!</a>
</li>
</ul>
Our CSS with Chained Pseudoselectors
Here’s the CSS in full:
.nav-menu .nav-item:first-child:after {
content: " (get reading!)";
}
.nav-menu .nav-item:not(:first-child):not(:last-child):after {
content: " (also check out!)";
}
.nav-menu .nav-item:after {
color:#fff;
font-size: .6em;
padding-left: .2rem;
}
.nav-menu .nav-item:last-child:after {
content: " (join!)";
}
.nav-menu .nav-item:nth-child(3) > a,
.nav-menu .nav-item:nth-child(3):after {
color: #aaa;
}Code language: CSS (css)
The code above exists to demo a few ways of complexly combining CSS pseudoselectors to target specific elements, somewhat like you could with a true programming language such as JavaScript.
Chaining Pseudo Classes and Pseudo Elements
The first thing to notice is that you can chain pseudo classes like :first-child with pseudo elements like :before and :after, as in this line:
.nav-menu .nav-item:nth-child(3):after {
color: #aaa;
}Code language: CSS (css)
This line targets:
- Elements inside an element with class
nav-menu - That have class
nav-item - That are the first child of their parent
The rule then uses a pseudo element to style the :after content of each targeted element. The result is that the specified content gets a light gray (#aaa) color.
Complex Selection with Pseudo Classes
Let’s take a look at this line:
.nav-menu .nav-item:not(:first-child):not(:last-child):after {
content: " (also check out!)";
}Code language: CSS (css)
This selects:
- Elements inside
.nav-menu - That have class
.nav-item - That are not the first or last child of their parent
The rule then puts content :after each element. The result is that “(also check out!)” appears after the second and third menu items.
Downsides of this Approach
ThThis approach is powerful, but it’s best used as a last resort when a site’s codebase is inaccessible or not worth refactoring. Everything above could have been done more simply by adding proper CSS classes.
Downsides include:
- It’s fragile. Adding or removing an element can break your carefully chained rules.
- It’s opaque. Injected content from CSS can confuse future developers.
- It’s not ideal for maintainability. HTML classes remain the cleaner long-term solution.
Still, these tricks can be a lifesaver when working with limited control over markup—something that happens often in the real world.
Now You Know Dark Magic!
Hopefully the power of chained pseudoselectors is starting to make sense: you can use only CSS to target very specific elements in a page of HTML markup, and do all kinds of things to them—even add specific content to the page.
I’ve also covered my bases with a warning that these tricks are second- or third-resort alternatives to being able to edit a page’s markup directly—but I’d be lying if I said this hasn’t been helpful for addressing multiple client needs.
Thanks for reading!





[…] and :after are two CSS important pseudoselectors. I’ve recently written in depth about pseudoselectors, so if you need a primer on how to use them, please check that article […]