The other day, I tried to add an underline style to an element on hover and it was not working. I did not realize I was styling a pseudo-element created by
:after. I preferred the single-colon variation
:after. It's as valid as
::after, life is short, and I am a lazy developer. My
:after:hover didn't work and I got frustrated.
Troubleshooting the issue got me thinking, can I combine other pseudo-elements like
::first-line? And if not, why? And why are both
::after legal anyway?
In this post, I will briefly go over the history of CSS pseudo-element and pseudo-class selectors, how they differ, how to combine them in everyday usage, and why I am an
::after only guy now.
Why is :after Valid CSS?
First, let's go over some CSS history. I first learned CSS in the early 2010s, when CSS 2.1 was 'The Standard' and single-colons roamed the land. While CSS 1 specified pseudo-class selectors like
:visited, it is CSS 2 that introduced pseudo-elements like
Back then, there was less emphasis on a pseudo-element vs pseudo-class differentiation – they both used the single-colon syntax. This is also when I made the mistake of conflating the two into a single pseudo-selector concept in my mind.
The two-colon syntax for pseudo-elements was introduced in CSS Selector 3. Due to the web's compatibility tenet, pseudo-element selectors from CSS 2.1 – including
:first-letter– are still valid today. However, new pseudo-elements like
::selection do not have a corresponding single-colon version – see for yourself by selecting each line in the below example:
Difference Between Pseudo-Class and Pseudo-Element
Pseudo-Class and Pseudo-Elements are both CSS selectors that have access to information outside of what the HTML DOM provides – hence the 'pseudo' prefix. Take the
:hover selector, it needs to know the user's current cursor location.
While pseudo-elements act on a DOM-like object, they create a pure CSS construct and do not actually affect the DOM. They also have an additional colon to denote this difference. The fact that
::after element is not an actual DOM node has accessibility implications, their content does not exist for screen readers and should be decorative only.
Readers thinking 'what about
:nth-child()?' – you got me. The previous two paragraphs aren't strictly true, some pseudo-class selectors are more 'tricks' or 'shortcuts' – I wish W3C would create a new name for those to make the distinction clear-cut.
To repeat, the conceptual difference between the two is that pseudo-class selectors act on an existing DOM element using some outside information while pseudo-element creates a new, 'not actually there' element for the render engine only. The
:visited pseudo-selector can only be used to select to existing anchor elements. While
::first-line create elements that otherwise cannot exist with the given DOM structure – hence the name.
Here's an example, the
::first-line pseudo-element is created by splitting the second
<span> element into two in order to change the color of its first word only.
How to Combine Pseudo-Class with Pseudo-Elements
What does knowing the difference between pseudo-classes and pseudo-elements tell us about their usage?
For one, pseudo-class selectors act on elements and can be chained just like any actual
:visited:hover is as valid as
On the other hand, pseudo-element selectors cannot be used interchangeably from an
element selector. You cannot chain them. Pseudo-elements are not actual elements – they cannot be a selector's target.
p::first-line::first-letter is not valid because
::first-line is not a real DOM element. Only use pseudo-elements at the end of a selector line.
As with all things CSS, the above is not an iron-clad rule.
::first-letter are currently in the editor's draft as modifications on the pseudo-element, and may eventually be officially included. However, these pseudo-element stacks should be treated as the exception and not the rule.
The same logic applies when combining pseudo-class and pseudo-element selectors. Pseudo-class results in DOM elements, so pseudo-elements can be added – why
p:hover:after is valid CSS. Switch the order, and we are applying pseudo-class to a non-existing element – why
p:after:hover is invalid.
Sidebar, if you enjoyed reading this article so far, please share it with others. If you really enjoyed it and don't want to miss the next one, you can sign up below to get new posts in your inbox weekly.
Here are some rules to remember next time you find yourself using the pseudo-selectors:
Rule 1: Pseudo-classes can be used like actual .class selectors and used freely. p:hover.class works.
Rule 2: Pseudo-elements should always be at the very end of your selector, they cannot be chained because they do not select real DOM elements.
Rule 3: Do not use the single-colon variation of pseudo-elements – even if it's easier – it's too easy to slip up when they share the same syntax. Plus, you now have to remember that
:after works but not
Before I go, here's a Code Pen with some pseudo-class and pseudo-element examples. Use it to test your knowledge. Read the CSS for each div and see if its behavior matches your expectation!