CSS content Property

The content property is used with the ::before and ::after pseudo-elements to inject generated content into an element's rendering tree.

selector::before { content: value; }
normal Sets the content to its default state, which is usually nothing for pseudo-elements.
none Prevents the pseudo-element from being rendered at all.
<string> Inserts specific text content wrapped in double or single quotes.
<image> Inserts an external resource like an image using the url() function.
counter() Displays the current value of a CSS counter.
attr() Retrieves and displays the value of a specific attribute from the host HTML element.
open-quote Inserts the appropriate opening quotation mark based on the language setting.
close-quote Inserts the appropriate closing quotation mark based on the language setting.

Code Examples

This basic example automatically appends the text "(External)" to any link with the specified class, helping users identify off-site links.

<style>
  .external-link::after {
    content: " (External)";
    color: #ff6600;
    font-size: 0.8em;
  }
</style>

<a href="https://google.com" class="external-link">Visit Search Engine</a>

This advanced example uses JS to update a data-attribute, while CSS uses attr() and attribute selectors to display a dynamic notification badge only when the count is above zero.

<style>
  .notification-bell {
    position: relative;
    display: inline-block;
    font-size: 30px;
  }
  .notification-bell::after {
    content: attr(data-count);
    position: absolute;
    top: -5px;
    right: -10px;
    background-color: #ff0000;
    color: #ffffff;
    font-size: 12px;
    padding: 2px 6px;
    border-radius: 10px;
    display: none;
  }
  .notification-bell[data-count]:not([data-count="0"])::after {
    display: block;
  }
</style>

<div id="bell" class="notification-bell" data-count="0">🔔</div>

<script>
  const bell = document.getElementById("bell");
  let count = 0;
  // Simulate receiving notifications
  setInterval(() => {
    count++;
    bell.setAttribute("data-count", count);
  }, 3000);
</script>

Pro Tip

You can combine multiple values in a single content property. For example, you can show a string and an attribute value together like: content: "Status: " attr(data-status);. This is an efficient way to create dynamic labels for dashboard items or status indicators without cluttering your HTML with extra span tags or divs.

Deep Dive

Think of the content property as a way to add "phantom" data to your page that doesn't exist in your HTML file. When you use it with pseudo-elements like ::before or ::after, you are telling the browser to create a temporary box inside the targeted element and fill it with whatever you specify. This box behaves much like a standard inline element. It is important to realize that this content is not part of the Document Object Model (DOM) tree, meaning it's purely for visual presentation and isn't easily manipulated by standard JavaScript selectors or found by search engine crawlers in the same way regular text is. It allows you to keep your HTML clean of decorative fluff while still providing visual cues or labels to the user.

Best Practices

Keep the content property strictly for decorative or supplementary purposes. Since it lives in the CSS, it should never contain vital information that a user needs for the site to function. Use the attr() function whenever possible to keep your CSS modular; this allows the HTML to provide the data while the CSS handles the placement. This is great for things like tooltips, badge counts, or UI icons where the logic might change but the style remains the same.

Common Pitfalls

The most common mistake is forgetting that the content property is required for pseudo-elements to show up. If you don't define content, even if it's just an empty string like "", the ::before or ::after element won't render. Also, beginners often forget to wrap text strings in quotes, which causes the CSS rule to break. Another thing to remember is that content injected this way is generally not selectable or copy-pasteable by the user, which can be frustrating if you put important text in there.

Accessibility

Content injected via CSS can be a major hurdle for accessibility. While some modern screen readers are getting better at announcing generated content, many still ignore it or handle it inconsistently. If you use the content property to add icons or text, ensure that the primary HTML element still has an aria-label or sufficient descriptive text so users with visual impairments aren't left in the dark. Never use it to convey critical instructions.

Dev Data Table: content property

default normal
animatable no
inherited no
experimental no
year_intro 1998
year_standard 1998
js_syntax_1 element.style.content = "'string value'";
js_syntax_2 element.style.setProperty("content", "'string value'");
js_note Since content is mostly used on pseudo-elements, you cannot target it directly using standard DOM selection. You must use getComputedStyle or update a CSS variable/data-attribute that the CSS references.
browsers { "Chrome": "1", "Edge": "12", "Firefox": "1", "Safari": "1", "Opera": "7", "Chrome Android": "18", "Safari on iOS": "1", "Samsung Internet": "1.0", "Opera Mobile": "10.1" }
results render here...