CSS has a property called content. It can only be used with the pseudo-elements ::after and ::before. It is written like a pseudo selector (with the colon), but it’s called a pseudo-element because it’s not actually selecting anything that exists on the page but adding something new to the page. This is what it looks like:
.email-address::before {
content: "Email address: ";
}
With this CSS in place, we could have this HTML:
<ul>
<li class="email-address">[email protected]</li>
</ul>
And the output would be like:
• Email address: [email protected]
Maybe that example doesn’t get you drooling, but pseudo-element content can be quite useful and do cool things. Let’s go through some ideas and considerations.
Hey! That’s content not design!
The first concern might be that of a separation-between-content-and-design purist. You are literally adding text content to the page with CSS content, and that breaks that barrier. The spec is done and the idea implemented, but that doesn’t mean it’s not worth discussing. If you have an opinion about CSS content and its use, please share in the comments.
I think it’s awesome and perfectly suited for CSS. Consider the example above where we preface all elements with a class of email-address with the text “Email address: “. That is a design decision, where for the clarity of content, it was decided that having that text before email addresses made the content more clear. Perhaps in a redesign of the site, there was less room where those email addresses are being displayed and it was decided that instead of prefacing them with text, a small icon would be used instead. This fits with the idea of CSS, in that the HTML content doesn’t need to change at all, this change could be solely accomplished with CSS.
I’m going to publish an article tomorrow with this kind of idea.
Using Special Characters
If you need to use a special character in the CSS content, it’s kinda weird. How I do it is I figure out what the ASCII number is for the symbol. This chart of glyphs is handy. So on that chart the copyright © symbol is © – so the ASCII number is 169. Then I drop that number in the Entity Conversion Calculator which will convert it into what you need for CSS.

Here’s some random useful ones:
\2018 – Left Single Smart Quote
\2019 – Right Single Smart Quote
\00A9 – Copyright
\2713 – Checkmark
\2192 – Right arrow
\2190 – Left arrow
Example Trick: Checkmark visited links
Mark your visited links with checkmarks:
#main-content a:visited::before {
content: "\2713 ";
}
Using Attributes
You are able to insert attributes of the elements you are targeting as content. For example, an anchor link might have a title attribute:
<a title="A web design community." href="https://css-tricks.com">CSS-Tricks</a>
You can access that title attribute from the content property like:
a:before {
content: attr(title) ": ";
}
Any attribute can be targeted as such, in the format attr(name-of-attribute). If you’d like to insert something into the HTML to use for a CSS content purpose (but nothing else), you could use the new data- attribute prefix in HTML5.
Example Trick: CSS3 tooltips
Tooltips for links based on the title attribute:
a {
color: #900;
text-decoration: none;
}
a:hover {
color: red;
position: relative;
}
a[title]:hover::after {
content: attr(title);
padding: 4px 8px;
color: #333;
position: absolute;
left: 0;
top: 100%;
white-space: nowrap;
z-index: 20;
border-radius: 5px;
box-shadow: 0px 0px 4px #222;
background-image: linear-gradient(#eeeeee, #cccccc);
}

This example uses the title attribute, and other examples like this that you find around the web also use the title attribute. It’s probably the correct one to use. However, do note that browsers have their own tooltip popups that they do. When that comes up, it will cover this, and look weird. I tried to take a screenshot of the issue but there it wasn’t letting me for some reason. There is no way to suppress this, other than just not using the title attribute. HTML5 data- attributes, again, could be useful here.
Points to consider
- Firebug can’t yet target pseduo elements. The web inspector in WebKit browsers can target them, but don’t show their property/values. I heard the IE dev tools could target them too, but not sure about the property/values.
- In WebKit, they have to be block level to be rotated. Firefox can rotate inline elements/pseudo-elements.
- In Firefox 3.0, pseudo elements can’t be absolutely positioned.
- They cannot be transitioned or animated.
Example Trick: Fancy email link popouts
I had an idea I wanted to try where you would have a vertical list of names, and as you moused over them, their email addresses would slide out from underneath them. To have the HTML be as clean as possible, I thought it would be cool to use the an ::after pseudo-element and a -webkit-transition to make it happen. But, alas, you cannot animate or transition a pseudo-element.

Using the ::after
/content method, I got an example working it just doesn’t slide out like I thought would be cool. Using <span>s I got the idea working, also in the demo page.
Example trick: display full links in print stylesheets
@media print {
#main-content a[href]:after { " (" attr(href) ") "; }
}
Browser support / Accessibility
All the major browsers (Firefox 3+, Safari 3+, Chrome 3+, Opera 10+, and Internet Explorer 8+) (See the full chart) support CSS content with the ::after/::before pseudo-elements and the spec is in its full candidate recommendation status.
Regarding accessibility, I’m just not 100% sure what the situation was. I was trying to use VoiceOver with Safari on my Mac with the email popout links demo. For best accessibility, I would think the goal would be to get it to read the whole text, including the CSS content being added. I had trouble getting it to do that, but I thought I did get it to do it once somehow. I was really bad at using VoiceOver and found it frustrating to get it to do what I was trying to do even at the most basic levels. If someone knows more about accessibility as it relates to CSS content, I’m sure we’d all love to know more.
Hi Chris,
Great article as always.
Although it might seem as if :before and :after don’t have much to do with design, there is a neat little trick you can accomplish, recently published on NetTuts:
Multiple borders with CSS
Your script have some bugs.
See screenshot.
That’s what I was trying to fight and thought I had it by making the hover state of the link go position: relative; But apparently that’s not enough for Opera, which may be treating the generated content’s z-index separately. Maybe try explicitly setting the z-index?
See a similar article here http://www.css3.info/tooltips-with-css3/ .
that’s probably because background gradient
just add background: #eee; :D before gradients
What about z-index?
It can be used to display images using data uri like
#myimg{content:url(“data:image/png;base64,abcdef…”)}
What is it good for you may ask? If you want to include your images in the document for portability, like an ebook.
But only works on webkit browsers like chrome and safari.
Remember that the selector needs to use :after or :before
If anyone wants to learn more about that data uri stuff, I have a little bit about it here: https://css-tricks.com/data-uris/
@Chris, it actually seems to work fine without the :before and :after on the img tag – just like George has shown. Has the content property changed behaviour since 2010?
I used it in these situations:
En-dash (–) in lists to replace bullets;
semicolon at end of lists and dot for last list element;
Quotation marks for blockquotes;
«Warning: this is phisher link» to my user css for one site than often faked (+ [href*=…] and :not[href=…])
Downside of using :after and :before is that Firebug doesn’t see it (hope they fix it in future).
Firebug does not see it, but WebKit’s inspect element does the trick (for this particular demo, at least).
However, it’d still be nice to have that feature in Firebug, too.
Pretty slick. I’m just discovering all of the awesomeness that :before and :after have to offer. These are great tricks. Thanks!
awesome.
This is why I love CSS Tricks, you always seem to uncover obscure attributes and then apply them to practical situations, great post Chris, thanks!
A couple of years ago I wanted to list some “Requirements” and “Objectives”.
It was to allow changing an objective to requirement (or vice-versa) but without reordering the numbering.
I used this:
and this:
I’ve used it to prevent no-loading images, together with longdesc in the html:
And to make rounded corners and shadows semantic elements of variable size with only 1 pic:
http://css.devillasbuenas.es/sombra-semantica/index.html
Un saludo
There is a way to solve ‘title’ tooltip problem:
Use jQuery to copy all of its content to a different attribute, say ‘tooltip’ and then use the CSS content property to display the ‘tooltip’ attribute.
What I like most about this is that if javascript is disabled or if the browser doesn’t support the CSS (IE6 and 7), you still have your native browser tooltip.
Definitely a good way to go. As I briefly mentioned in the article, I might go with “data-tooltip” (valid) instead of “tooltip” (invalid).
Do note that, if I recall correctly, that method will make the tooltip dependant on javascript, which kind of the point here was make it a CSS-tooltip.
(asuming the css will refer to the non-“title” attribute)
Not really…. because the links will have the title attribute just like normal which will have a normal tooltip without JavaScript. But with JavaScript, that attribute will be changed to something that won’t trigger the browser default tooltip but can still be used for the CSS tooltip.
Hi Chris, great article thank you.
I’ve been fascinated with the “content” issue for a while now. Initially I needed it to solve an issue we had with our forms, instead of changing an error to red, we’d want to output error text before a field classed as error, this works perfectly for those occasions.
However, being a government organisation we needed to conform to accessibility standards and that’s where it came a bit unstuck really.
I made a post at the accessify forums and a kind gentleman named Jordan Gray advised that it wasn’t very accessible!
That said, for my personal needs, it’s great!
Thanks again for the great article! :)
VERY good to know, thank you for sharing Jonathan!
I think the first sentence from Jordan’s comment is worth reposting:
It’s a real shame that screen readers haven’t caught up yet. If they were up to standards, as designers, we could use this to it’s full potential as we know it can be so useful.
Lets go back and review something real quick.
CSS is getting @var variables
CSS has almost basic if/then logic with @media queries
(if device width is less than 600px then…)
CSS is getting math calculations
(calc())
CSS can add nodes to the dom
(.email-address:before)
CSS can access dom node attributes
(content: attr(title) “: “;)
Am I the only one seeing good reason to create a locked down, simplified subset of real javascript specifically designed to handle the job of styling content?
@Chris Coyier:
As shown in this screenshot (http://i.imgur.com/jFbdF.png) Safari 5’s Web Inspector does fully show and interacts with the Pseudo selectors.
Can you explain how you got to that point? I have Safari 5 and I tried the latest WebKit nightly and I couldn’t figure out how to actually target the pseudo element.
Once again I learn about an obscure corner of CSS I’d forgotten about – thanks.
However, I think content belongs to HTML, and the styling to CSS – I can imagine overuse of this making things very confusing. Imagine using tables in your HTML and content in your CSS…
I think there are instances where you want to add content primarily for styling.
This article interested me because I had just created a list of links where I wanted a right arrow to display at the end of each link.
Within my HTML I had
→
after every link, which I don’t think is an ideal solution. This method is much cleaner. There’s definitely situations (maybe rare) when content is style.You are just the css master!!
Thanks a lot to you and to all this useful commentators too!!
Hey Chris, this convertor might make using special characters a bit easier :)
Just drop in the character and hit convert!
You are the best Chris. Love CSS Tricks
I make heavy use of content property in print style sheets :)
Cool! This is just great, I like the content property in print style sheets.
You da man Chris, lovely css work
yeah, pseudo selectors are nice. I had a designer tell me that she wanted quote marks before and after a tagline, but they needed to be Times instead of cufon replaced.
One usage for “content” is using it alongside with counters to number the images, like “Figure 1”, “Table 3-2”. It could easily be done via CSS and it’s easier than to put that in markup.
Also Chris, you use it in your code and pre tags to show the visitors the language of the code. It’s pretty neat.
great article on a very less spoken property of css !
Hi Chris, this is a very interesting post. It looks like there’s a lot the :before and :after pseudo selectors can do. I havn’t really used them in any of my sites… maybe im missing a trick, I seem to be coping without them so far.
I look forward to seeing this particular blog subject develop further…
Cheers.
oohh! that is so cool. im going to use this!
thanks!
One thing that I tried before but failed at was being able to insert html content after a certain element. A good example of this would be the ribbon effect where you have to enter in two extra divs after a main div. It would be awesome to be able to figure out a way to enter in those extra divs using the after pseudo selector.
One thing I found unintuitive about setting it for links is that you CANNOT use
a:after {...}
but must rather be referenced asa[href]:after {...}
although I can understand why that might be. Also, it should be stated that when using hover or active states, as well as classes and IDs, you should use the syntax in the following order, since it wasn’t very obvious at first:a[attr]:state#id.class:after
For not to mention the extremely useful float clearing abilities
This is actually pretty nice way of going about it. Well done Chris!!
This article would have saved me some time about a year ago when I was creating a WordPress theme based off of the default one. They stuck in some content items into the CSS to add the >>. It took me forever to figure out where it was coming from.
Honestly, I probably would have used jQuery to add the Email Address in front. I get the not cluttering up the HTML, but is there a reason why you would use CSS over js on something like that?
This is awesome. I’ve been looking for a simple way to indicate multilevel menu in WordPress, and this is just perfect for that :D
.haschildren a:before {
content: “»”;
}
and the remove » from sub menu
.haschildren ul .page_item a:before {
content: normal;
}
Thanks!
Damn it…can’t make it work with WordPress 3….
In addition to the conversion tool Brendan linked to, http://www.digitalmediaminute.com/reference/entity is also worth checking out.
thanks Chris. still thinking way to apply this to my current proj but definitely a new thing for me :)
Thanks for the great article!! I was just searching last week for more information on using the css content property and couldn’t find anything that helped. This pretty much answered all my questions. Awesome!
I’ve used the CSS Content attribute to power my debug stylesheet, which I toggle on/off via a bookmarklet. Works great and the development team can quickly see any potential issues in our markup structures.
Is there a way to get support this property for IE 6 and 7?
I think this would be great for the recent hype around responsive web design.
For example, a situation where icons in a sidebar would be displayed on a 3X3 grid on a larger resolution, but on a more narrow layout they would be in a list with words next to them.
Great use of the content tag Chris. I fiddled with this a bit and with the following bit of jQuery, the flyouts work in Firefox as well (no css animations support yet).
Combined with Modernizr, you could detect CSS3 animations and use where supported and use the above jQuery where they’re not. This way you could provide the same functionality to all users.
I put a demo up here
Sure you need an escaped hex value for special characters? I just enter any Unicode symbols directly. It works, and it’s more readable.
Of course, don’t forget to serve your CSS files as UTF-8, via an HTTP header. To be sure, add
@charset 'UTF-8';
to the very top of the CSS file.Awesome article, I really like the CSS tooltip snippet.
I noticed that in your code you (accidentally) had:
z-index: 20px;
but I think you meant:
z-index: 20;
as far as I know, that property is only allowed integer values, even though it probably just ignores the ‘px’.
Chris,
I was trying this out on my most recent website trying to overlay the alt text of an image on top of it. For whatever reason, I could not get it to work and had to take the text and put it inside the title attribute on the parent list element instead. I got it to work and it looks really nice, but I was wondering if you’ve come across an instance, or knew of a reason why this would not work with the image alt text as the CSS content.
Hi guys,
Mark, I am interest to know if you found a solution to your problem, as I am looking to do the very same thing.
Thanks,
Cristi
I’m trying to insert:
<div class=”rightArrow”> </div>
as content :before The div tags are showing. I’ve tried 03c… and <…. but no dice. Any idea how to insert tags?
Thanks for the help.~
Very nice article to change the tooltip style without js or jquery.
Just one question, is it possible to use “transition” properties of CSS for animate the apparence of the tooltip?
Hello,
I know that this article is somehow old, but stilt the content fresh to any one doesn’t know about content property
I have a comment regarding animating or transition a pseudo element:
I managed to do that but couldn’t control yet fading in & out as i want.
Do this is already updated by now in css ?
i will post a link to link to the page where i applied that
The spec says that the
content: '';
declaration should only work on pseudo elements, but this example works in the latest webkit browsers.( “http://www.projectevolution.com/activity/replace-image-when-printing/””projectevolution.com/activity/replace-image-when-printing”)
Print preview this page in Chrome to see the difference: (http://preview.python.org “preview.python.org”)
Do you know, is webkit being lenient on purpose, or is the spec becoming more relaxed?
This is cool, but is there more…
ref: sass, check hint.css at http://kushagragour.in/lab/hint/
Definitely ripping a hole in the fabric of the internet (for ie6, but…)
(absolute actor holding fixed actor) held by relative actor…
(see Chris’ reply at top of this comment group)
can attr’d tooltip be tipped relative to page,
so tooltip display is liquid, centered, responsive?
Demo above please, Chris.
Do you know why tit doesnt work ?
[main role=”main” class=”container” data-margin=”20vw”]
thanks
I think you can only get attribute values from an element to be the ‘content’ property of generated content, not to be any value for any css property.
Working on a responsive design where I want to add an ndash to second level nav children and mdash to the third level, when the device is less than 480px.
What I am having trouble with is removing this when I get to the breakpoint for devices over 768px. Can anyone tell me how to remove this styling?
Your help is greatly appreciated.
To reset, I usually declare content: ”;
The printing links idea is cool (but missing “content: “). Worth extending, perhaps, to exclude links within the page:
@media print {
.main a[href]:after { content: ” (” attr(href) “) “; }
.main a[href^=”#”]:after { content: “”; }
}
Cool stuff…
Today I was looking into how GitHub uses CSS content to decorate their anchors. Go to https://github.com/lodash/lodash/blob/master/README.md and hover of one of the titles. The CSS seems to be like this:
The
F05C
glyph, however, is odd. If I runalert('\F05C')
I get a square box. Also, according to http://www.fileformat.info/info/unicode/char/f05c/index.htm it’s an Asian glyph…What am I missing?
Most likely an iconfont, as they usually map their chracters to glyphs outside of the Latin set.
Thanks a lot, ‘iconfont’ was the missing piece. I had never heard of this before, dunno what it is but searching the Internet for ‘GitHub’ and ‘iconfont’ led to https://github.com/styleguide/css/7.0. Case closed.
This is a very interesting concept. Thank you for detailing this. I have been looking for a way to put full coding that you use over and over again into a class with css. For example, I have a Reviews page on my site that lists dozens of reviews. By each review I have social links to allow the user to post the review to each of various social sites. I would prefer to not replicate all of that code for each occurrence of the social links.
Do you think there is a way to put the code into a class and call the class for each occurrence?
No, a server-side include would be the best way to handle that. Injecting executable HTML into the DOM via CSS and the content property would be a big security loophole were it allowed. I believe CSS will not allow that to happen. If it does allow it — say, if you base64 encode the html and then have the CSS render it somehow — that would make it possible to do script injection via CSS.
Thanks very much for getting back to me so quickly. Do you have any ideas of how to minimize html code when you need to replicate it many times on a page? I see you mentioned “server side include”. Not familiar with that so could you point me in a direction for help or other ideas? THANKS John
Most programming languages — ASP, PHP, etc… — have the means by which an external file can be referenced and “included” into the current working file. This is great for reusing little bits of code in more than one place. If you are not working with a dynamic server programming language, than a similar thing can be achieved with jQuery and AJAX, where a bit of code can be injected into specified parts of the template.
In your case, I would imagine you have an empty div like so: “ and then a bit of javascript that looks for those elements and adds the proper HTML inside the div. This makes further sense for “progressive enhancement”, as when the browser does not have JS enabled (or its broken), nothing happens and the page is not cluttered up with social share code that can not be executed.
I can’t tell you how to do it exactly, as much depends on how you have your site set up, but that is the idea.
The difference between content and design relies at the very first concepts of the www. Performance and readability.
The performance is the reason that we (developers designers) optimize EVERYTHING!!!!
Readability or otherwise serving the purpose of the message, is archived with a process called ‘design’.
Now having those two notions clear on mind, and the current state of technology/technics, you resolve the issue of the distinction between content and design.
And this is not a humble opinion…
Lazaros
Cool trick for tool tips. Using data-title is recommended. If you want simple tooltips use this method instead of jQuery plugins.
Thanks Chris, every CSS questions asked in google leads to your articles and every time the best answer is found.