CSS Pseudo-Elements Module Level 4

Editor’s Draft,

More details about this document
This version:
https://drafts.csswg.org/css-pseudo-4/
Latest published version:
https://www.w3.org/TR/css-pseudo-4/
Previous Versions:
Feedback:
CSSWG Issues Repository
Inline In Spec
Editors:
Elika J. Etemad / fantasai (Apple)
(Adobe Systems Inc.)
Former Editor:
Daniel Glazman (Disruptive Innovations)
Suggest an Edit for this Spec:
GitHub Editor
Issues List:
Tracked in Editor’s Draft
Test Suite:
https://wpt.fyi/results/css/css-pseudo/

Abstract

This CSS module defines pseudo-elements, abstract elements that represent portions of the CSS render tree that can be selected and styled.

CSS is a language for describing the rendering of structured documents (such as HTML and XML) on screen, on paper, etc.

Status of this document

This is a public copy of the editors’ draft. It is provided for discussion only and may change at any moment. Its publication here does not imply endorsement of its contents by W3C. Don’t cite this document other than as work in progress.

Please send feedback by filing issues in GitHub (preferred), including the spec code “css-pseudo” in the title, like this: “[css-pseudo] …summary of comment…”. All issues and comments are archived. Alternately, feedback can be sent to the (archived) public mailing list [email protected].

This document is governed by the 18 August 2025 W3C Process Document.

The following features are at-risk, and may be dropped during the CR period:

“At-risk” is a W3C Process term-of-art, and does not necessarily imply that the feature is in danger of being dropped or delayed. It means that the WG believes the feature may have difficulty being interoperably implemented in a timely manner, and marking it as such allows the WG to drop the feature if necessary when transitioning to the Proposed Rec stage, without having to publish a new Candidate Rec without the feature first.

1. Introduction

This section is informative.

Pseudo-elements represent abstract elements of the document beyond those elements explicitly created by the document language. Since they are not restricted to fitting into the document tree, they can be used to select and style portions of the document that do not necessarily map to the document’s tree structure. For instance, the ::first-line pseudo-element can select content on the first formatted line of an element after text wrapping, allowing just that line to be styled differently from the rest of the paragraph.

Each pseudo-element is associated with an originating element and has syntax of the form ::name-of-pseudo. This module defines the pseudo-elements that exist in CSS and how they can be styled. For more information on pseudo-elements in general, and on their syntax and interaction with other selectors, see [SELECTORS-4].

Note: As a reminder, pseudo-elements cannot be chained together unless explicitly allowed. For example, ::marker::before is not allowed; but ::before::marker is.

Note: Level 4 is the first version of the CSS Pseudo specification under this name. It was branched from the Selectors 3 [SELECTORS-3] specification, and thus started at level 4, with the remainder of Selectors 3 becoming Selectors 4 [SELECTORS-4].

2. Typographic Pseudo-elements

2.1. First-Line Text: the ::first-line pseudo-element

The ::first-line pseudo-element represents the contents of the first formatted line of its originating element.

Tests
The rule below means “change the letters of the first line of every p element to uppercase”:
p::first-line { text-transform: uppercase }

The selector p::first-line does not match any real document element. It instead matches a pseudo-element that the user agent will automatically insert at the beginning of every p element.

Note: Note that the length of the first line depends on a number of factors, including the width of the page, the font size, etc.

For example, given an ordinary HTML [HTML5] paragraph such as:
<P>This is a somewhat long HTML paragraph
that will be broken into several lines.
The first line will be styled
by the ‘::first-line’ pseudo-element.
The other lines will be treated
as ordinary lines in the paragraph.</P>

Depending on the width of the element, its lines might be broken as follows:

THIS IS A SOMEWHAT LONG HTML PARAGRAPH THAT
will be broken into several lines. The first
line will be styled by the ‘::first-line’
pseudo-element. The other lines will be
treated as ordinary lines in the paragraph.

or alternately as follows:

THIS IS A SOMEWHAT LONG
HTML paragraph that will
be broken into several
lines. The first line will
be styled by the
‘::first-line’ pseudo-
element. The other lines
will be treated as ordinary
lines in the paragraph.

2.1.1. Finding the First Formatted Line

In CSS, the ::first-line pseudo-element can only have an effect when attached to a block container:

Note: The first formatted line can be an empty line. For example, the first line of the p in <p><br>First… doesn’t contain any letters. Thus the word “First” is not on the first formatted line, and will not be affected by p::first-line.

Note: The first line of a block container that does not itself participate in a block formatting context cannot be the first formatted line of an ancestor element. Thus, in <DIV><P STYLE="display: inline-block">Hello<BR>Goodbye</P> etcetera</DIV> the first formatted line of the DIV is not the line “Hello”, but rather the line that contains that entire inline block.

When a first formatted line is represented by multiple ::first-line pseudo-elements, they are nested in the same order as their originating elements. The inline-level contents of this line—​including its root inline box fragment—​are nested within the innermost ::first-line pseudo-element.

Consider the following markup:
<DIV>
  <P>First paragraph</P>
  <P>Second paragraph</P>
</DIV>

If we assume a fictional tag sequence to represent the elements’ ::first-line pseudo-elements, it would be something like:

<DIV>
  <P><DIV::first-line><P::first-line>First paragraph</P::first-line></DIV::first-line></P>
  <P><P::first-line>Second paragraph</P::first-line></P>
</DIV>

2.1.2. Styling the ::first-line Pseudo-element

The ::first-line pseudo-element’s generated box behaves similar to that of an inline-level box, but with certain restrictions. The following CSS properties apply to a ::first-line pseudo-element:

User agents may apply other properties as well except for the following excluded properties:

Note: Setting line-height on ::first-line inherits to the fragment of the root inline box that wraps the contents of the first line, and therefore can both increase and decrease the height of the first line box.

2.1.3. Inheritance and the ::first-line Pseudo-element

During CSS inheritance, the fragment of a child that occurs on the first line inherits any standard inherited properties—​except the properties excluded above—​from the ::first-line pseudo-element. For all other properties, including all custom properties [CSS-VARIABLES-1], inheritance is from the non-pseudo parent. (The portion of a child element that does not occur on the first line always inherits from the non-pseudo parent.)

In the common case (of standard inherited CSS properties), inheritance into and from a ::first-line pseudo-element can be understood by writing out a fictional tag sequence to represent ::first-line. Consider the earlier example; in case of the first rendering, the fictional tag sequence would be:
<P><p::first-line>This is a somewhat long HTML paragraph
that</p::first-line> will be broken into several lines.
The first line will be styled
by the ‘::first-line’ pseudo-element.
The other lines will be treated
as ordinary lines in the paragraph.</p>

And in the case of the second rendering:

<p><p::first-line>This is a somewhat long</p::first-line> HTML paragraph
that will be broken into several lines.
The first line will be styled
by the ‘::first-line’ pseudo-element.
The other lines will be treated
as ordinary lines in the paragraph.</p>
If a pseudo-element breaks up a real element, the effect can often be described by a fictional tag sequence that closes and then re-opens the element. Suppose we mark up the earlier example with a span element encompassing the first sentence:
<p><span>This is a somewhat long HTML paragraph
that will be broken into several lines.</span>
The first line will be styled
by the ‘::first-line’ pseudo-element.
The other lines will be treated
as ordinary lines in the paragraph.</p>

The effect of the first rendering would be similar to the following fictional tag sequence:

<p><p::first-line><span>This is a somewhat long HTML paragraph
that</span></p::first-line><span> will be broken into several lines.</span>
The first line will be styled
by the ‘::first-line’ pseudo-element.
The other lines will be treated
as ordinary lines in the paragraph.</p>

2.2. First-Letter Text: ::first-letter pseudo-element and its ::prefix and ::suffix children

Tests
A drop-cap initial letter, including the opening quotation mark before it.

The ::first-letter pseudo-element represents the first Letter, Number, or Symbol (Unicode category L*, N*, or S*) typographic character unit on the first formatted line of its originating element (the first letter) as well as its associated punctuation. Collectively, this text is the first-letter text. The ::first-letter pseudo-element can be used to create “initial caps” and “drop caps”, which are common typographic effects.

For example, the following rule creates a 2-line drop-letter on every paragraph following a level-2 header, using the initial-letter property defined in [CSS-INLINE-3]:
h2 + p::first-letter { initial-letter: 3; }

Note: The first letter may in fact be a digit, e.g., the “6” in “67 million dollars is a lot of money.”

To allow independent styling of the first letter itself and its adjacent punctuation, associated preceding punctuation is represented by the ::prefix sub-pseudo-element of the ::first-letter pseudo-element (::first-letter::prefix); and associated following punctuation is represented by the ::suffix sub-pseudo-element of the ::first-letter pseudo-element (::first-letter::suffix). See § 2.2.1 First Letters and Associated Punctuation, below.

2.2.1. First Letters and Associated Punctuation

As explained in CSS Text 3 § 1.4 Characters and Letters, a typographic character unit can include more than one Unicode codepoint. For example, combining characters must be kept with their base character. Also, languages may have additional rules about how to treat certain letter combinations. In Dutch, for example, if the letter combination "ij" appears at the beginning of an element, both letters should be considered within the ::first-letter pseudo-element. [UAX29] When selecting the first letter, the UA should tailor its definition of typographic character unit to reflect the first-letter traditions of the ::first-letter pseudo-element’s containing block’s content language.

Preceding and following punctuation must also be included as part of the first-letter text in the ::first-letter pseudo-element as follows:

Informally, the first-letter text’s pattern here can be roughly (ignoring the exclusions from Zs) represented as a “regular expression” P (Zs|P)*)? (L|N|S) ((Zs|P−(Ps|Pd))* (P−(Ps|Pd))?—​or, alternatively, [P] [Zs P]*)? [L N S] ([Zs [P--[Ps Pd]]]* [P--[Ps Pd]])?—​where the Unicode category abbreviation represents the set of all typographic character units belonging to that category.
P P Zs L N S P − (Ps ∪ Pd) P − (Ps ∪ Pd) Zs

See CSS Text 3 § 1.4 Characters and Letters and CSS Text 3 § E Characters and Properties for more information on typographic character units and their Unicode properties. [CSS-TEXT-3]

2.2.2. Finding the First-Letter Text

As with ::first-line, the ::first-letter pseudo-element can only have an effect when attached to a block container. Its first-letter text is the first such inline-level content participating in the inline formatting context of its originating element’s first formatted line, if it is not preceded by any other in-flow content (such as images or inline tables) on its line.

For this purpose, any marker boxes are ignored, as if they were out-of-flow. However, if an element has in-flow ::before or ::after content, the first-letter text is selected from the content of the element including that generated content.

Example: After the rule p::before {content: "Note: "}, the selector p::first-letter matches the "N" of "Note".

If no qualifying text exists, then there is no first-letter text and no ::first-letter pseudo-element.

Note: When the first formatted line is empty, ::first-letter will not match anything. For example, in this HTML fragment: <p><br>First... the first line doesn’t contain any letters, so ::first-letter doesn’t match anything. In particular, it does not match the “F” of “First”, which is on the second line.

Note: As with ::first-line, the first-letter text of a block container that does not participate in a block formatting context cannot be the first-letter text of an ancestor element. Thus, in <DIV><P STYLE="display: inline-block">Hello<BR>Goodbye</P> etcetera</DIV> the first letter of the DIV is not the letter “H”. In fact, the DIV doesn’t have a first letter.

Any portion of the first-letter text that is wrapped to the next line no longer forms part of the ::first-letter pseudo-element.

2.2.3. Inheritance and Box Tree Structure of the First-Letter Pseudo-elements

The ::first-letter pseudo-element is wrapped immediately around the first-letter text it represents, even if that text is in a descendant. When a first-letter text is represented by multiple ::first-letter pseudo-elements, they are nested in the same order as their originating elements. Inheritance behaves accordingly.

Consider the following markup:
<div>
  <p><span>The first few words</span>
  and the rest of the paragraph.
</div>

If we assume a fictional tag sequence to represent the elements’ ::first-letter pseudo-elements, it would be something like:

<div>
  <p><span><div::first-letter><p::first-letter>T</…></…>he first few words</span>
  and the rest of the paragraph.
</div>

If any ::first-letter::prefix or ::first-letter::suffix pseudo-elements exist, they are nested within the innermost ::first-letter, and otherwise interpreted similar to ::first-letter itself.

Consider the following markup:
<div>
  <p><span>“The first few words</span>
  and the rest of the quotation.
</div>

If we assume a fictional tag sequence to represent the elements’ ::first-letter pseudo-elements, it would be something like:

<div>
  <p><span><div::first-letter><p::first-letter><div::first-letter::prefix><p::first-letter::prefix></…></…>T</…></…>he first few words</span>
  and the rest of the paragraph.
</div>

If the characters that would form the first-letter text are not all in the same element (as the ‘T in <p>‘<em>T...), the user agent may create the ::first-letter pseudo-element (and its ::prefix or ::suffix sub-elements, if any) from one of the elements, or all elements, or simply not create the pseudo-element(s). Additionally, if the first-letter text is not at the start of the line (for example due to bidirectional reordering, or due to a list item marker with list-style-position: inside), then the user agent is not required to create the pseudo-element(s).

A ::first-letter pseudo-element is contained within any ::first-line pseudo-elements, and thus inherits (potentially indirectly) from ::first-line, the same as any inline box on the same line.

2.2.4. Styling the First-Letter Pseudo-elements

In CSS a ::first-letter pseudo-element (and its ::prefix and ::suffix sub-elements) is similar to an inline box. The following properties apply to ::first-letter, ::first-letter::prefix, and ::first-letter::suffix pseudo-elements:

User agents may apply other properties as well. However, in no case may the application of such unlisted properties to ::first-letter change what first-letter text is represented by that ::first-letter.

Note: In previous levels of CSS, user agents were allowed to choose a line height, width, and height based on the shape of the letter, to approximate font sizes; and to take the glyph outline into account when performing layout. The possibility of such loosely-defined magic has been intentionally removed, as it proved to be a poor solution for the intended use case (drop caps and raised caps), yet caused interoperability problems. See initial-letter in [CSS-INLINE-3] for explicitly handling drop caps and raised caps.

3. Highlight Pseudo-elements

3.1. Selecting Highlighted Content: the ::selection, ::search-text, ::target-text,