Understanding the CSS ‘content’ Property

Share this article

If you are a front-end developer there is a good chance that you have heard about pseudo-elements as well as CSS’s content property. I won’t discuss pseudo-elements in depth here, but I suggest you take a look at this Louis Lazaris’ article on Smashing Magazine if you are unfamiliar with the concept or need a quick refresher.

In this article, we’ll focus on the content property. CSS’s content property works with the ::before and ::after pseudo-elements (which can use either single- or double-colon synax). The property is used to insert generated content in a web page and it is fully supported in all major browsers.

Basic Syntax for the content Property

The syntax for the content property is broken down as follows, with each of the values represented:

p::before {
  content: normal|counter|attr|string|open-quote|url|initial|inherit;

The CSS differs slightly from value to value. For example, to use the attr() value with ::before or ::after, you need to write CSS like the following:

p::after {
  content: " (" attr(title) ")";

That’s just one example, and more on that later. In the following sections I will discuss each value, including attr().

Value: none or normal

When set to none, the pseudo-element is not generated. If you set it to normal it computes to none for the ::before and ::after pseudo-elements.

p::before {
  content: normal;

p::after {
  content: none;

This kind of thing might be used in nested elements that already have a pseudo-element defined but you want to override the pseudo-element in certain contexts.

Value: <string>

This value sets the content to a string and can be any text content. If using non-latin characters, the characters need to be encoded. Let’s look at examples of each. Consider the following HTML:

<h2>Tutorial Categories</h2>
  <li>HTML and CSS</li>
  <li class="new">Sass &amp; Less</li>

<p class="copyright">SitePoint, 2015<p>

And then the following CSS:

.new::after {
  content: " New!";
  color: green;

.copyright::before {
  content: "\00a9  ";

Here we are inserting text content into one of the list items, and also inserting an encoded character (in this case, the copyright symbol) into the paragraph element.

See the Pen Content Property with Text by SitePoint (@SitePoint) on CodePen.

A string value must have quotes surrounding it and these can be either single or double quotes.

Value: <uri>

The <uri> value comes in handy when you are interested in displaying some sort of media, which you can do by pointing to an external resource (for instance an image). If for some reason the resource or image can’t be displayed, it is either ignored or some placeholder takes its place. Let’s look at some code that demonstrates the use of this value.

Here is the HTML:

<a class="sp" href="https://www.sitepoint.com/">SitePoint</a>

And this is the CSS to show SitePoint’s favicon along with some text:

.sp::before {
  content: url(https://www.sitepoint.com/favicon.ico);

See the Pen Content Property with url() by SitePoint (@SitePoint) on CodePen.

Value: counter() or counters()

This value is the most complex value that can be used with the content property. It is written as one of two different functions — counter() or counters(). For a more thorough discussion of CSS counters, check out this article. But here is a brief overview.

In the case of the first function, counter() , the generated text is the value of the innermost counter of the name that you specify in scope at this pseudo-element. It is formatted in decimal by default but can also be formated in roman numerals.

The other function, counters(name, string), is similar but represents all counters with the specified name (the first parameter) in the order from outermost to innermost. All these values are separated by the specified string (the second parameter). If you specify the name for the counter variable as none, inherit, or initial the declaration is ignored.

Here is an example to illustrate how to use a counter:

<h2>Name of First Four Planets</h2>
<p class="planets">Mercury</p>
<p class="planets">Venus</p>
<p class="planets">Earth</p>
<p class="planets">Mars</p>

And here is the CSS:

.planets {
  counter-increment: planetIndex;

.planets::before {
  content: counter(planetIndex) ". ";

This will number the items with generated content, similar to an ordered list. In this case, we could have easily used an ordered list. These types of counters are much handier when the items being numbered are interspersed among other elements.

Here is a demo that uses CSS counters with the counter() function in use.

Value: attr()

As shown earlier, the attr() function will insert the value of the specified attribute, which is the sole parameter. If the concerned element has no attribute, an empty string is returned.

Here’s an example:

  <li><a href="https://www.sitepoint.com/html-css/">HTML and CSS</a></li>
  <li><a href="https://www.sitepoint.com/javascript">JavaScript</a></li>
  <li><a href="https://www.sitepoint.com/mobile/">Mobile</a></li>

With the above HTML, the CSS below will show the value of the href attribute in parentheses next to the linked text:

a::after {
  content: " (" attr(href) ") ";

See the Pen Content Property using attr() by SitePoint (@SitePoint) on CodePen.

This trick is often used in print style sheets, to allow links to be visible in printed web pages.

Value: open-quote or close-quote

When set to one of these values, the content property generates an opening or closing quotation mark. It’s customarily used along with the <q> element, but you can use it with any element. So you might do something like the following:

blockquote::before {
  content: open-quote;

blockquote::after {
  content: close-quote;

See the Pen Content Property with open/close quotes by SitePoint (@SitePoint) on CodePen.

The close-quote value will work only with the ::after pseudo-element (for obvious reasons) and it will not produce anything if a value of open-quote is not also present on the same element using ::before.

Value: no-open-quote or no-close-quote

The no-open-quote value removes the opening quote from the specified element and the no-close-quote removes the closing quote. You might wonder how these values are useful. Look at the following HTML:

<p><q>A wise man once said: <q>Be true to yourself,
but don't listen to those who say <q>Don't be true to 
yourself.</q></q> That is good advice.</q></p>

Take note that in the above paragraph, the speaker is quoting someone (“A wise man…”) who, in turn, is also quoting others (“those who say…”). So we have quotes nested 3 levels deep. Grammatically, there is a correct way to handle this. If using generated content, here is how we can ensure the quotes are nested correctly:

q {
  quotes: '“' '”' '‘' '’' '“' '”';

q::before { 
  content: open-quote;

q::after {
  content: close-quote;

The first selector defines the kinds of quotes we want to use, three levels deep, using the quotes property. Then we insert the quotes as content using pseudo-elements. This is similar to what we did in the previous section.

See the Pen Content property with nested quotes by SitePoint (@SitePoint) on CodePen.

But what if we want the second level of quotes to be ignored and not inserted, for whatever reason? We can use the no-open-quote and no-close-quote values to override them:

.noquotes::before {
  content: no-open-quote;

.noquotes::after {
  content: no-close-quote;

In this case, I’ve added a class of noquotes to the second level quote. This ensures that the quote nesting is still recognized, but the quotes don’t appear for that element. So the third level quote in that paragraph will have double curly quotes around it, rather than single curly quotes.

See the Pen Content property with no-open-quote/no-close-quote by SitePoint (@SitePoint) on CodePen.


I hope this tutorial helped you to understand each of the values of the content property a little better and how they can be used in various scenarios.

Check out the resources below for more info:

Frequently Asked Questions (FAQs) about CSS Content Property

What is the CSS content property and how is it used?

The CSS content property is a powerful tool that allows you to insert generated content in a page’s layout. It’s typically used with the ::before and ::after pseudo-elements to add decorative content via CSS, rather than including it in the HTML. For instance, you can use the content property to insert quotation marks around a blockquote, or to add a decorative image before a heading. The value of the content property can be a string, a URL, a counter, or even the value of an attribute.

Can the CSS content property be used with elements other than ::before and ::after?

The CSS content property is primarily used with the ::before and ::after pseudo-elements. However, it can also be used with the ::marker pseudo-element, which represents the marker box of a list item. This allows you to customize the appearance of list bullets or numbers.

How can I use the CSS content property to display attribute values?

The CSS content property can use the attr() function to display the value of an HTML attribute. For example, you can use it to display the value of the href attribute of a link, making the actual URL visible to the user. The syntax would be content: attr(href).

Can I use the CSS content property to insert images?

Yes, the CSS content property can use the url() function to insert an image. The image will be inserted at the point where the ::before or ::after pseudo-element is placed. For example, content: url(image.jpg) will insert the image named image.jpg.

How can I use counters with the CSS content property?

The CSS content property can use the counter() or counters() function to display a counter. This can be useful for automatically numbering headings or sections in a document. You would first use the counter-reset property to create or reset a counter, and the counter-increment property to increment it. Then you can use content: counter(myCounter) to display the current value of the counter.

Can I use special characters with the CSS content property?

Yes, you can use special characters with the CSS content property by using their Unicode. For example, content: “\2022” will insert a bullet point. Remember to always start the Unicode with a backslash ().

Can the CSS content property affect SEO?

Content added via CSS is typically considered decorative and not content that should be indexed by search engines. Therefore, it’s best to use the CSS content property for decorative content and not for content that is important for SEO.

Can I use the CSS content property to insert HTML?

No, the CSS content property cannot be used to insert HTML. It can only insert text, images, counters, and attribute values. If you need to insert HTML, you should do it directly in the HTML document or use JavaScript.

Is the CSS content property supported in all browsers?

The CSS content property is widely supported in all modern browsers, including Chrome, Firefox, Safari, and Edge. However, it may not be fully supported in older versions of Internet Explorer.

Can I animate the CSS content property?

No, the CSS content property cannot be animated. This is because it’s not a property that has a range of values, but rather it sets a specific value. If you need to create an animation, consider using other CSS properties that can be animated, such as opacity or transform.

Gajendar SinghGajendar Singh
View Author

Gajendar is a web developer with a keen interest in learning new things in web development. He has been developing websites for five years and occasionally writes tutorials on topics he feels confident about.

content propertycss contentcss quoteslearn-advanced-cssLouisLpseudo-elements
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week
Loading form