Grow your CSS skills. Land your dream job.

CSS Content

Published by Chris Coyier

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">chriscoyier@gmail.com</li>
</ul>

And the output would be like:

• Email address: chriscoyier@gmail.com

Maybe that example doesn't get you drooling, but pseduo 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 &#169; - 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.


Not pretty, but it works.

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="http://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: 20px;
  -moz-border-radius: 5px; 
  -webkit-border-radius: 5px;  
  border-radius: 5px;  
  -moz-box-shadow: 0px 0px 4px #222;  
  -webkit-box-shadow: 0px 0px 4px #222;  
  box-shadow: 0px 0px 4px #222;  
  background-image: -moz-linear-gradient(top, #eeeeee, #cccccc);  
  background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #eeeeee),color-stop(1, #cccccc));
  background-image: -webkit-linear-gradient(top, #eeeeee, #cccccc);  
  background-image: -moz-linear-gradient(top, #eeeeee, #cccccc);  
  background-image: -ms-linear-gradient(top, #eeeeee, #cccccc);  
  background-image: -o-linear-gradient(top, #eeeeee, #cccccc);  
}

Now 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.

View Demo

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 it's 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.

More

A Whole Bunch of Amazing Stuff Pseudo Elements Can Do

Comments

  1. Toni Kukurin

    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

  2. Your script have some bugs.
    See screenshot http://s02.radikal.ru/i175/1007/0e/cc1e62243791.png .

  3. What about z-index?

  4. 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: http://css-tricks.com/data-uris/

    • Chris Howard

      @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?

  5. Pretty slick. I’m just discovering all of the awesomeness that :before and :after have to offer. These are great tricks. Thanks!

  6. #main-content a:visited:before {
       content:  "\2713 ";
    }

    awesome.

  7. 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!

  8. Roflo

    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:

    li { display: list-item; list-style: none; counter-increment: req; }
    li.obje:before { content: "<O-" counter(req) "> "; }
    li.requ:before { content: "<R-" counter(req) "> "; }
    ol.rolist { counter-reset: req; } 

    and this:

    <ol class="rolist">
        <li class="requ"> Req 1 </li>
        <li class="requ"> Req 2 </li>
        <li class="requ"> Req 3 </li>
        <li class="obje"> Obj 4 </li>
        <li class="requ"> Req 5 </li>
    </ol>
  9. I’ve used it to prevent no-loading images, together with longdesc in the html:

    img:after {
       content: 'DESCRIPCIÓN DE LA IMAGEN NO CARGADA: ' attr(longdesc);
    }

    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

  10. 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:

      Screenreaders should not read out the content of the before and after pseudoelements, because they are intended for purely decorative effect—meaningful content must be represented in your HTML somehow.

    • 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.

  11. 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?

  12. Gringer

    @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.

  13. 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.

  14. You are just the css master!!

    Thanks a lot to you and to all this useful commentators too!!

  15. You are the best Chris. Love CSS Tricks

  16. I make heavy use of content property in print style sheets :)

  17. Cool! This is just great, I like the content property in print style sheets.

  18. You da man Chris, lovely css work

  19. 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.

  20. 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.

  21. great article on a very less spoken property of css !

  22. 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.

  23. oohh! that is so cool. im going to use this!

    thanks!

  24. 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.

  25. One thing I found unintuitive about setting it for links is that you CANNOT use a:after {...} but must rather be referenced as a[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

  26. For not to mention the extremely useful float clearing abilities

    .clearfix:after {
        content: ".";
        display: block;
        height: 0;
        clear: both;
        visibility: hidden;
    }
  27. This is actually pretty nice way of going about it. Well done Chris!!

  28. 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?

  29. CJ

    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!

  30. In addition to the conversion tool Brendan linked to, http://www.digitalmediaminute.com/reference/entity is also worth checking out.

  31. thanks Chris. still thinking way to apply this to my current proj but definitely a new thing for me :)

  32. 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!

  33. 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.

  34. Is there a way to get support this property for IE 6 and 7?

  35. Reginald RP
    Permalink to comment#

    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.

  36. 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.

  37. Joe
    Permalink to comment#

    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’.

  38. 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.

    • Cristi
      Permalink to comment#

      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

  39. Dan B
    Permalink to comment#

    I’m trying to insert:

    <div class=”rightArrow”> </div>

    as content :before The div tags are showing. I’ve tried 03c… and &lt;…. but no dice. Any idea how to insert tags?

  40. Permalink to comment#

    Thanks for the help.~

  41. 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?

  42. Permalink to comment#

    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

  43. Permalink to comment#

    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?

  44. Mark Stewart
    Permalink to comment#

    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.

  45. erickb
    Permalink to comment#

    Do you know why tit doesnt work ?

    [main role="main" class="container" data-margin="20vw"]

    main{
        margin-top:attr(data-margin) !important;
    }
    

    thanks

    • Rowan
      Permalink to comment#

      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.

  46. 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.

  47. Kim SJ
    Permalink to comment#

    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: “”; }
    }

  48. Permalink to comment#

    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:

    .octicon-link:before {
      content: '\f05c';
    }
    

    The F05C glyph, however, is odd. If I run alert('\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?

  49. JJackson

    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.

    • JJackson

      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.

  50. Lazaros Kosmides

    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

  51. 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.

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".