Menu

CSS ::before and ::after Pseudo-Elements With Examples

::before and ::after insert generated content as the first and last child of an element, straight from CSS. Learn the content property, why an empty content is required, positioning them, attr() tooltips, counters, and where they do not work.

This page includes runnable editors - edit, run, and see output instantly.

What ::before and ::after do

::before inserts a box as the first child of an element and ::after as the last child. What goes in the box is set by the content property. Nothing is added to the HTML: the browser generates it from CSS.

The HTML holds only 9 and Tests pass. The dollar sign, the "/ month" label and the check mark all come from CSS. They inherit the element's styles (font, color) unless you override them, as the examples above do.

Syntax

selector::before {
  content: "text";   /* required */
  /* any other styles */
}

selector::after {
  content: "";       /* empty string: a box with no text */
}
content valueResult
"text"A string. Use "\A" for a line break (with white-space: pre), "\2192" for a Unicode character (→)
""An empty box, for shapes and decorations
attr(data-label)The value of an attribute of the element
counter(step)The value of a CSS counter
open-quote / close-quoteQuotation marks for the element's language
none or normalNo pseudo-element (normal is the default)

content is mandatory. Without it, ::before and ::after are never created, however many other properties you give them.

Decorative shapes with content: ""

With content: "", the pseudo-element is an empty box you can size and color. It is inline by default, and an inline box ignores width and height, so give it display: block, inline-block, or position: absolute:

Positioning a pseudo-element

For a decoration that sits on top of or around the element, use position: absolute on the pseudo-element and position: relative on the element itself, so the offsets are measured from the element:

Note the order when combining with a pseudo-class: the state comes first, then the pseudo-element, .link:hover::after, never .link::after:hover.

Reading attributes with attr(): a tooltip

attr() pulls an attribute's value into content, so one rule can show different text on every element:

A hover-only tooltip is invisible to keyboard and touch users. For anything important, put the text in the page, or also show it on :focus with tabindex="0" on the element.

Numbering with counters

CSS counters and ::before number things without typing the numbers:

Add or remove a step and the numbers update themselves.

Where ::before and ::after do not work

Pseudo-elements are inserted inside the element, between its tags. Elements that have no inside for them cannot show them:

  • <img>, <video>, <iframe> (replaced elements)
  • <input>, <select>, <textarea> (form controls), and <br>

Some browsers draw them on a checkbox or a broken image, but that is not consistent. Put the pseudo-element on a wrapper instead: <span class="field"><input></span> and style .field::after.

Common mistakes

  • No content. The single most common reason nothing shows. Add content: "".
  • Sizing an inline pseudo-element. width and height do nothing until you set display: block, inline-block or position: absolute.
  • Wrong order with a state. a:hover::after works; a::after:hover does not.
  • Absolute position without a positioned parent. The pseudo-element then aligns to some distant ancestor. Add position: relative to the element.
  • Putting real content in CSS. Text in content cannot be selected or copied. Keep information in the HTML.

Frequently Asked Questions

What does ::before do in CSS?

It creates a pseudo-element that is the first child of the selected element, filled with whatever the content property says. ::after does the same as the last child. Neither exists in the HTML, so they are handy for icons, labels and decorations.

Why is my ::before or ::after not showing?

The most common reason is a missing content property: without it the pseudo-element is not generated. Use content: "" for an empty decorative box. Other causes: an inline pseudo-element with a width and height (give it display: block or inline-block), or an element such as <img> or <input> that cannot hold them.

What is the difference between :before and ::before?

None in behavior. ::before with two colons is the current syntax that marks pseudo-elements; :before with one colon is the older CSS2 spelling that browsers still accept for before, after, first-line and first-letter.

Can I use ::before on an img or input?

Not reliably. Pseudo-elements are inserted inside the element's content, and replaced elements like <img> and form controls like <input> have no content box for them. Wrap the element in a <span> or <div> and put the pseudo-element on the wrapper.

Can screen readers read CSS ::before content?

Generally yes: text in content is usually included in the accessible name, but it cannot be selected or copied. Keep real information in the HTML and use pseudo-elements for decoration and small visual cues.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED