Learning To Use The :after And :before Pseudo-Elements In CSS
If you’ve been keeping tabs on various Web design blogs, you’ve probably noticed that the :before
and :after
pseudo-elements have been getting quite a bit of attention in the front-end development scene — and for good reason. In particular, the experiments of one blogger — namely, London-based developer Nicolas Gallagher — have given pseudo-elements quite a bit of exposure of late.
To complement this exposure (and take advantage of a growing trend), I’ve put together what I hope is a fairly comprehensive run-down of pseudo-elements. This article is aimed primarily at those of you who have seen some of the cool things done with pseudo-elements but want to know what this CSS technique is all about before trying it yourself.
Although the CSS specification contains other pseudo-elements, I’ll focus on :before
and :after
. So, for brevity, I’ll say “pseudo-elements” to refer generally to these particular two.
What Does A Pseudo-Element Do?
A pseudo-element does exactly what the word implies. It creates a phoney element and inserts it before or after the content of the element that you’ve targeted.
The word “pseudo” is a transliteration of a Greek word that basically means “lying, deceitful, false.” So, calling them pseudo-elements is appropriate, because they don’t actually change anything in the document. Rather, they insert ghost-like elements that are visible to the user and that are style-able in the CSS.
Basic Syntax
The :before
and :after
pseudo-elements are very easy to code (as are most CSS properties that don’t require a ton of vendor prefixes). Here is a simple example:
#example:before {
content: "#";
}
#example:after {
content: ".";
}
There are two things to note about this example. First, we’re targeting the same element using #example:before
and #example:after
. Strictly speaking, they are the pseudo-elements in the code.
Secondly, without the content
property, which is part of the generated content module in the specification, pseudo-elements are useless. So, while the pseudo-element selector itself is needed to target the element, you won’t be able to insert anything without adding the content
property.
In this example, the element with the id example
will have a hash symbol placed “before” its content, and a period (or full stop) placed “after” its content.
Some Notes On The Syntax
You could leave the content
property empty and just treat the pseudo-element like a content-less box, like this:
#example:before {
content: "";
display: block;
width: 100px;
height: 100px;
}
However, you can’t remove the content
property altogether. If you did, the pseudo-element wouldn’t work. At the very least, the content
property needs empty quotes as its value.
You may have noticed that you can also code pseudo-elements using the double-colon syntax (::before
and ::after
), which I’ve discussed before. The short explanation is that there is no difference between the two syntaxes; it’s just a way to differentiate pseudo-elements (double colon) from pseudo-classes (single colon) in CSS3.
One final point regarding the syntax. Technically, you could implement a pseudo-element universally, without targeting any element, like this:
:before {
content: "#";
}
While the above is valid, it’s pretty useless. The code will insert a hash symbol before the content in each element in the DOM. Even if you removed the <body>
tag and all of its content, you’d still see two hash symbols on the page: one in the <html>
element, and one in the <body>
tag, which the browser automatically constructs.
Characteristics Of Inserted Content
As mentioned, the content that is inserted is not visible in the page’s source. It’s visible only in the CSS.
Also, the inserted element is by default an inline element (or, in HTML5 terms, in the category of text-level semantics). So, to give the inserted element a height, padding, margins and so forth, you’ll usually have to define it explicitly as a block-level element.
This leads well into a brief description of how to style pseudo-elements. Look at this graphic from my text editor:
In this example, I’ve highlighted the styles that will be applied to the elements inserted before and after the targeted element’s content. Pseudo-elements are somewhat unique in this way, because you insert the content and the styles in the same declaration block.
Also note that typical CSS inheritance rules apply to the inserted elements. If you had, for example, a font stack of Helvetica, Arial, sans-serif
applied to the <body>
element of the document, then the pseudo-element would inherit that font stack the same as any other element would.
Likewise, pseudo-elements don’t inherit styles that aren’t naturally inherited from parent elements (such as padding and margins).
After Before What?
Your hunch on seeing the :before
and :after
pseudo-elements might be that the inserted content will be injected before and after the targeted element. But, as alluded to above, that’s not the case.
The content that’s injected will be child content in relation to the targeted element, but it will be placed “before” or “after” any other content in that element.
To demonstrate this, look at the following code. First, the HTML:
<p class="box">Other content.</p>
And here’s the CSS that inserts a pseudo-element:
p.box {
width: 300px;
border: solid 1px white;
padding: 20px;
}
p.box:before {
content: "#";
border: solid 1px white;
padding: 2px;
margin: 0 10px 0 0;
}
In the HTML, all you would see is a paragraph with a class of box
, with the words “Other content” inside it (the same as what you would see if you viewed the source on the live page). In the CSS, the paragraph is given a set width, along with some padding and a visible border.
Then we have the pseudo-element. In this case, it’s a hash symbol inserted “before” the paragraph’s content. The subsequent CSS gives it a border, along with some padding and margins.
Here’s the result viewed in the browser:
The outer box is the paragraph. The border around the hash symbol denotes the boundary of the pseudo-element. So, instead of being inserted “before” the paragraph, the pseudo-element is placed before the “Other content” in the paragraph.
Inserting Non-Text Content
I mentioned briefly that you can leave the content
property’s value as an empty string or insert text content. You basically have two additional options of what to include as the value of the content
property.
First, you can include a URL that points to an image, just as you would do when including a background image in the CSS:
p:before {
content: url(image.jpg);
}
Notice that the quotes are missing. If you wrapped the URL reference in quotes, then it would become a literal string and insert the text “url(image.jpg)” as the content, instead of inserting the image itself.
Naturally, you could include a Data URI in place of the image reference, just as you can with a CSS background.
You also have the option to include a function in the form of attr(X)
. This function, according to the spec, “returns as a string the value of attribute X for the subject of the selector.”
Here’s an example:
a:after {
content: attr(href);
}
What does the attr()
function do? It takes the value of the specified attribute and places it as text content to be inserted as a pseudo-element.
The code above would cause the href
value of every <a>
element on the page to be placed immediately after each respective <a>
element. This could be used in a print style sheet to include full URLs next to all links when a document is printed.
You could also use this function to grab the value of an element’s title
attribute, or even microdata values. Of course, not all of these examples would be practical in and of themselves; but depending on the situation, a specific attribute value could be practical as a pseudo-element.
While being able to grab the title
or alt
text of an image and display it on the page as a pseudo-element would be practical, this isn’t possible. Remember that the pseudo-element must be a child of the element to which it is being applied. Images, which are void (or empty) elements, don’t have child elements, so it wouldn’t work in this case. The same would apply to other void elements, such as <input>
.
Dreaded Browser Support
As with any front-end technology that is gaining momentum, one of the first concerns is browser support. In this case, that’s not as much of a problem.
Browser support for :before
and :after
pseudo-elements stacks up like this:
- Chrome 2+,
- Firefox 3.5+ (3.0 had partial support),
- Safari 1.3+,
- Opera 9.2+,
- IE8+ (with some minor bugs),
- Pretty much all mobile browsers.
The only real problem (no surprise) is IE6 and IE7, which have no support. So, if your audience is in the Web development niche (or another market that has low IE numbers), you can probably go ahead and use pseudo-elements freely.
Pseudo-Elements Aren’t Critical
Fortunately, a lack of pseudo-elements will not cause huge usability issues. For the most part, pseudo-elements are generally decorative (or helper-like) content that will not cause problems in unsupported browsers. So, even if your audience has high IE numbers, you can still use them to some degree.
A Couple Of Reminders
As mentioned, pseudo-element content does not appear in the DOM. These elements are not real elements. As such, they are not accessible to most assistive devices. So, never use pseudo-elements to generate content that is critical to the usability or accessibility of your pages.
Another thing to keep in mind is that developer tools such as Firebug do not show the content generated by pseudo-elements. So, if overused, pseudo-elements could cause maintainability headaches and make debugging a much slower process.
Update: As mentioned in the comments, you can use Chrome’s developer tools to view the styles associated with a pseudo-element, but the element will not appear in the DOM. Also, Firebug is adding pseudo-element support in version 1.8.
That covers all of the concepts you need in order to create something practical with this technique. In the meantime, for further reading on CSS pseudo-elements, be sure to check out some of the articles that we’ve linked to in this piece.
Further Reading
- A Guide To CSS Pseudo-Classes And Pseudo-Elements
- Taming Advanced CSS Selectors
- Semantic CSS With Intelligent Selectors
- CSS Specificity: Things You Should Know