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 value | Result |
|---|---|
"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-quote | Quotation marks for the element's language |
none or normal | No 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. Addcontent: "". - Sizing an inline pseudo-element.
widthandheightdo nothing until you setdisplay: block,inline-blockorposition: absolute. - Wrong order with a state.
a:hover::afterworks;a::after:hoverdoes not. - Absolute position without a positioned parent. The pseudo-element then aligns to some distant ancestor. Add
position: relativeto the element. - Putting real content in CSS. Text in
contentcannot 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.