CSS content Property

Beginner
5 min read
Updated: Oct 2026
3 examples
Live preview

Definition and Usage

The content property inserts generated content — usually on ::before and ::after. You can add a string, pull an attribute with attr(), show a counter, or use quote keywords.

The initial value is normal (on ::before / ::after that means nothing is generated). It is not inherited. Use it for decoration and labels you could live without if CSS failed — keep essential meaning in HTML.

Baseline for common values.

Not deprecated. Strings, none, attr(), counters, and quotes are widely available. Alternative text after / (e.g. content: "★" / "star") has limited support — prefer HTML for critical labels.

CSS Syntax

syntax.css
content: normal | none | <string> | attr() | counter() | open-quote | …;

Study starters: content: " New!"; · content: attr(data-label); · content: none;

Property Values

ValueDescriptionTry it
<string>Quoted text inserted into the pseudo-elementTry it
noneNo pseudo-element is generatedTry it
attr()Uses an HTML attribute value as the generated textTry it
counter()Shows a CSS counter value (needs increment/reset)Try it
open-quoteInserts an opening quote from the quotes propertyTry it
normalInitial value. On ::before/::after acts like noneTry it

How to Use content

Target a pseudo-element, then say what it should show.

  1. Pick an element and attach ::before or ::after.
  2. Set content to a string, attr(…), counter(…), or a quote keyword.
  3. Style the pseudo-element with color, margin, or font if needed.
  4. Keep meaning in HTML — use generated text for optional decoration.
PieceRequired?Role
Pseudo-elementYes (for study)::before or ::after is where beginners use content
content valueYesWithout a value other than normal/none, nothing appears
HTML textFor meaningScreen readers and SEO rely on real markup
Counters / quotesOptionalNeed matching counter or quotes setup

Live Preview

The label uses ::before. Only its content value changes.

none and normal remove the generated prefix. Strings appear before the HTML text.

Pseudo-element demo

content: "★ "

CSS Examples

Each snippet focuses on content with ::before or ::after. Use Try It Yourself for a live editor.

Example 1 — String on ::after

Append a short label after matching items.

CSS
<style>
  .new::after {
    content: " New!";
    color: #b91c1c;
  }
</style>

<p class="new">Halloween Stories</p>
Try It Yourself

How It Works

  1. Target ::after. The pseudo-element sits after the paragraph’s text.
  2. Set a string. content: " New!" is the lesson — note the leading space.
  3. Optional color. Red makes the badge easy to see; it is not required for content.
  4. HTML stays the title. The word “New!" is decoration from CSS only.

Example 2 — attr() Label

Pull text from a data attribute into ::before.

CSS
<style>
  p::before {
    content: attr(data-label) ": ";
    font-weight: bold;
  }
</style>

<p data-label="Tip">Save often while you code.</p>
Try It Yourself

How It Works

  1. Store a value in HTML. data-label="Tip" lives on the element.
  2. Read it with attr(). content: attr(data-label) ": " builds “Tip: ”.
  3. Show it before the text. ::before places that string at the start.
  4. Change the attribute to change the label. No need to edit the CSS string each time.

Example 3 — counter()

Number items with a CSS counter displayed via content.

CSS
<style>
  ol {
    counter-reset: step;
    list-style: none;
  }

  li {
    counter-increment: step;
  }

  li::before {
    content: counter(step) ". ";
  }
</style>

<ol>
  <li>Learn content</li>
  <li>Try attr()</li>
  <li>Add counters</li>
</ol>
Try It Yourself

How It Works

  1. Reset the counter. counter-reset: step starts counting on the list.
  2. Increment each item. counter-increment advances step per li.
  3. Print with content. content: counter(step) ". " is the lesson for this page.
  4. Hide default markers. list-style: none avoids double numbering.

Edge Cases & Pitfalls

Generated content is powerful — and easy to misuse.

a11y

Not for critical information

Generated text may be ignored by assistive tech. Put required meaning in HTML, not only in content.

target

Needs a pseudo-element for strings

Beginners should set content on ::before or ::after. A string on a plain div will not replace its child text as you might expect.

quotes

Missing quotes look empty

open-quote / close-quote need a sensible quotes value (or browser defaults) or the marks may be wrong or invisible.

alt

Slash alt text is limited

content: url(icon.png) / "Label" is useful when supported, but support is incomplete. Prefer real HTML for important labels.

space

Spaces must be inside the string

Write content: " New!" with a leading space if you need a gap. CSS does not add one automatically.

Browser Support

content is a browser CSS property for generated content. Node.js, Deno, and Bun do not apply it.

✓ Baseline · Widely available

content

Strings, none, normal, attr(), counters, and quotes are widely supported. Alternative text after / and some newer functions have limited support.

100% Common values
Google Chrome All versions
Full support
Mozilla Firefox All versions
Full support
Apple Safari All versions
Full support
Microsoft Edge All versions
Full support
Opera All versions
Full support
Samsung Internet All versions
Full support
Internet Explorer 8+
Full support
content Widely supported

Bottom line: Safe for everyday ::before / ::after strings, attr(), and counters. Treat slash alt text as progressive. It is not a runtime API.

Key Takeaways

  • Role: define generated content for pseudo-elements like ::before and ::after.
  • Initial value: normal (acts like none on those pseudo-elements).
  • Common tools: strings, attr(), counter(), and quote keywords.
  • Accessibility: decoration only — keep real meaning in HTML.

One line: content fills ::before / ::after with generated text — use it for polish, not for essential copy.

Frequently Asked Questions

It defines generated content for ::before and ::after (and some other pseudo-elements). Strings, attr(), counters, quotes, and images are common values.
The initial value is normal. On ::before and ::after, normal computes to none, so nothing is generated until you set a real value.
For beginners, treat content as a pseudo-element tool. Setting a string on a regular element does not replace its HTML text the way students often expect.
It is often missing from the accessibility tree. Do not put critical information only in content — put that in HTML.
Write content: attr(data-label) on ::before or ::after to pull an attribute value into generated text.
Strings, none, normal, attr(), counters, and quotes are Baseline and widely available. Alternative text after a slash has limited support. It is a browser CSS property, not a Node.js feature.

Next: counter-increment

Learn how to advance CSS counters that content: counter() can display.

counter-increment tutorial →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

8 people found this page helpful