[css4-pseudoelements] message topic
" Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C liability, trademark and document use rules apply.
This document extracts the notion of pseudo-elements from the Selectors 3
specification and proposes to extend it to be able to create and style
an arbitrary number of pseudo-elements before, after
and inside (for instance in the case of CSS columns) an element.
This document is a submission by Adobe Systems Inc. to the CSS Working Group for discussion.
Pseudo-elements create abstractions about the document tree beyond those
specified by the document language. For instance, document languages do
not offer mechanisms to access the first letter or first line of an
element's content. Pseudo-elements allow authors to refer to this
otherwise inaccessible information. Pseudo-elements may also provide
authors a way to refer to content that does not exist in the source
document (e.g., the ::before
and ::after
pseudo-elements give access to generated content).
Pseudo-elements can be placed anywhere relatively to the element creating them although the state of the art currently allows only the following :
The notation for a pseudo-element is made of two colons (::
)
followed by the name of the pseudo-element, possibly immediately followed
by a block of parenthesis containing comma-separated arguments.
For compatibility with existing style sheets, user agents must also
accept the previous one-colon notation for pseudo-elements introduced in
CSS levels 1 [CSS1] and 2 [CSS21]
(namely :first-line
, :first-letter
, :before
and :after
). This compatibility is not allowed for the new
pseudo-elements introduced in this specification.
The ::first-line
pseudo-element describes the contents of
the first formatted line of an element.
CSS example:
p::first-line { text-transform: uppercase }
The above rule means "change the letters of the first line of every p
element to uppercase".
The selector p::first-line
does not match any real
document element. It does match a pseudo-element that conforming user
agents will insert at the beginning of every p
element.
Note that the length of the first line depends on a number of factors, including the width of the page, the font size, etc. Thus, 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 identified by a fictional tag sequence. The other lines will be treated as ordinary lines in the paragraph.</P>
the lines of which happen to be broken as follows:
THIS IS A SOMEWHAT LONG HTML PARAGRAPH THAT will be broken into several lines. The first line will be identified by a fictional tag sequence. The other lines will be treated as ordinary lines in the paragraph.
This paragraph might be "rewritten" by user agents to include the fictional
tag sequence for ::first-line
. This fictional tag
sequence helps to show how properties are inherited.
<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 identified by a fictional tag sequence. The other lines will be treated as ordinary lines in the paragraph.</P>
If a pseudo-element breaks up a real element, the desired effect can
often be described by a fictional tag sequence that closes and then
re-opens the element. Thus, if we mark up the previous paragraph with a span
element:
<P><SPAN class="test"> This is a somewhat long HTML paragraph that will be broken into several lines.</SPAN> The first line will be identified by a fictional tag sequence. The other lines will be treated as ordinary lines in the paragraph.</P>
the user agent could simulate start and end tags for span
when inserting the fictional tag sequence for ::first-line
.
<P><P::first-line><SPAN class="test"> This is a somewhat long HTML paragraph that will </SPAN></P::first-line><SPAN class="test"> be broken into several lines.</SPAN> The first line will be identified by a fictional tag sequence. The other lines will be treated as ordinary lines in the paragraph.</P>
In CSS, the ::first-line
pseudo-element can only have an
effect when attached to a block-like container such as a block box,
inline-block, table-caption, or table-cell.
The first formatted line of an element may occur inside a block-level
descendant in the same flow (i.e., a block-level descendant that is not
out-of-flow due to floating or positioning). For example, the first line
of the DIV
in <DIV><P>This
line...</P></DIV>
is the first line of the P
(assuming that both P
and DIV
are
block-level).
The first line of a table-cell or inline-block 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".
Note Note that the
first line of the p
in this fragment: <p><br>First...
doesn't contain any letters (assuming the default style for br
).
The word "First" is not on the first formatted line.
A User-Agent should act as if the fictional start tags of the ::first-line
pseudo-elements were nested just inside the innermost enclosing
block-level element. (Since CSS1 and CSS2 were silent on this case,
authors should not rely on this behavior.) For example, the fictional tag
sequence for
<DIV> <P>First paragraph</P> <P>Second paragraph</P> </DIV>
is
<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>
The ::first-line
pseudo-element is similar to an
inline-level element, but with certain restrictions. The following CSS
properties apply to a ::first-line
pseudo-element: font
properties, color property, background properties, ‘word-spacing
’,
‘letter-spacing
’, ‘text-decoration
’,
‘vertical-align
’, ‘text-transform
’,
‘line-height
’. User-Agents may apply other
properties as well.
During CSS inheritance, the portion of a child element that occurs on the
first line only inherits properties applicable to the ::first-line
pseudo-element from the ::first-line
pseudo-element. For all
other properties inheritence is from the non-pseudo-element parent of the
first line pseudo element. (The portion of a child element that does not
occur on the first line always inherits from the parent of that child.)
The ::first-letter
pseudo-element represents the first
letter of an element, if it is not preceded by any other content (such as
images or inline tables) on its line. The ::first-letter pseudo-element
may be used for "initial caps" and "drop caps", which are common
typographical effects.
Punctuation (i.e, characters defined in Unicode in the "open" (Ps), "close" (Pe), "initial" (Pi). "final" (Pf) and "other" (Po) punctuation classes), that precedes or follows the first letter should be included. [UNICODE]
The ::first-letter
also applies if the first letter is in
fact a digit, e.g., the "6" in "67 million dollars is a lot of money."
Note In some cases
the ::first-letter
pseudo-element should include more than
just the first non-punctuation character on a line. For example, combining
characters must be kept with their base character. Additionally, some
languages may have specific rules about how to treat certain letter
combinations. The User-Agent definition of ::first-letter
should include at least the default grapheme cluster as defined by UAX29
and may include more than that as appropriate. 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]
If the letters that would form the ::first-letter
are not
in the same element, such as "‘T" in
<p>'<em>T...
,
the User-Agent may create a ::first-letter
pseudo-element
from one of the elements, both elements, or simply not create a
pseudo-element.
Similarly, if the first letter(s) of the block are not at the start of the line (for example due to bidirectional reordering), then the User-Agent need not create the pseudo-element(s).
Example:
The following CSS and HTML example
illustrates how overlapping pseudo-elements may interact. The first
letter of each P element will be green with a font size of ’24pt'. The
rest of the first formatted line will be ‘blue
’
while the rest of the paragraph will be ‘red
’.
p { color: red; font-size: 12pt } p::first-letter { color: green; font-size: 200% } p::first-line { color: blue } <P>Some text that ends up on two lines</P>
Assuming that a line break will occur before the word "ends", the fictional tag sequence for this fragment might be:
<P> <P::first-line> <P::first-letter> S </P::first-letter>ome text that </P::first-line> ends up on two lines </P>
Note that the ::first-letter
element is inside the ::first-line
element. Properties set on ::first-line
are inherited by ::first-letter
,
but are overridden if the same property is set on ::first-letter
.
The first letter must occur on the first
formatted line. For example, in this HTML fragment: <p><br>First...
the first line doesn't contain any letters and ::first-letter
doesn't match anything (assuming the default style for br
in
HTML 4). In particular, it does not match the "F" of "First."
In CSS, the ::first-letter
pseudo-element applies to
block-like containers such as block, list-item, table-cell, table-caption,
and inline-block elements. Note: A
future version of this specification may allow this pseudo-element to
apply to more display types.
The ::first-letter
pseudo-element can be used with all such
elements that contain text, or that have a descendant in the same flow
that contains text. A User-Agent should act as if the fictional start tag
of the ::first-letter pseudo-element is just before the first text of the
element, even if that first text is in a descendant.
Example:
The fictional tag sequence for this HTML fragment:
<div> <p>The first text.
is:
<div> <p><div::first-letter><p::first-letter>T</...></...>he first text.
In CSS the first letter of a table-cell or inline-block cannot be the
first letter 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.
If an element is a list item (‘display: list-item
’),
the ::first-letter
applies to the first letter in the
principal box after the marker. User-Agents may ignore ::first-letter
on list items with ‘list-style-position: inside
’.
If an element has ::before
or ::after
content,
the ::first-letter
applies to the first letter of the
element including that content.
Example:
After the rule p::before {content: "Note: "}
, the
selector p::first-letter
matches the "N" of "Note".
In CSS a ::first-line pseudo-element is similar to an inline-level
element if its ‘float
’ property is ‘none
’;
otherwise, it is similar to a floated element. The following properties
that apply to ::first-letter
pseudo-elements: font
properties, ‘text-decoration
’, ‘text-transform
’,
‘letter-spacing
’, ‘word-spacing
’
(when appropriate), ‘line-height
’, ‘float
’,
‘vertical-align
’ (only if ‘float
’
is ‘none
’), margin properties, padding
properties, border properties, color property, background properties.
User-Agents may apply other properties as well. To allow User-Agents to
render a typographically correct drop cap or initial cap, the User-Agent
may choose a line-height, width and height based on the shape of the
letter, unlike for normal elements.
Example:
This CSS and HTML example shows a possible rendering of an initial cap.
Note that the ‘line-height
’ that is
inherited by the ::first-letter
pseudo-element is 1.1, but
the User-Agent in this example has computed the height of the first
letter differently, so that it doesn't cause any unnecessary space
between the first two lines. Also note that the fictional start tag of
the first letter is inside the span, and thus the font
weight of the first letter is normal, not bold as the span:
p { line-height: 1.1 } p::first-letter { font-size: 3em; font-weight: normal } span { font-weight: bold } ... <p><span>Het hemelsche</span> gerecht heeft zich ten lange lesten<br> Erbarremt over my en mijn benaeuwde vesten<br> En arme burgery, en op mijn volcx gebed<br> En dagelix geschrey de bange stad ontzet.
The following CSS will make a drop cap initial letter span about two lines:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <HTML> <HEAD> <TITLE>Drop cap initial letter</TITLE> <STYLE type="text/css"> P { font-size: 12pt; line-height: 1.2 } P::first-letter { font-size: 200%; font-weight: bold; float: left } SPAN { text-transform: uppercase } </STYLE> </HEAD> <BODY> <P><SPAN>The first</SPAN> few words of an article in The Economist.</P> </BODY> </HTML>
This example might be formatted as follows:
The fictional tag sequence is:
<P> <SPAN> <P::first-letter> T </P::first-letter>he first </SPAN> few words of an article in the Economist. </P>
Note that the ::first-letter
pseudo-element tags abut the
content (i.e., the initial character), while the ::first-line
pseudo-element start tag is inserted right after the start tag of the
block element.
In order to achieve traditional drop caps formatting, user agents may approximate font sizes, for example to align baselines. Also, the glyph outline may be taken into account when formatting.
The ::before
and ::after
pseudo-elements can
be used to describe generated content before or after an element's
content. They are explained in CSS 2.1 [CSS21].
When the ::first-letter
and ::first-line
pseudo-elements are applied to an element having content generated using ::before
or ::after
pseudo-elements, they apply to the first letter or line of the
element including the generated content.
A CSS rule
using ::before
or ::after
creates a pseudo-element only if
the computed
values of the 'content'
property and the 'flow-from'
property [CSS3-REGIONS] are not both 'none'.
More than one ::before or ::after pseudo-element can be created or accessed by adding a positive integer called the ordinal in parentheses.
The ordinal 1 has a special meaning:
::before(1)
and ::before
are strictly equivalent::after(1)
and ::after
are also strictly equivalent.::before()
and ::after()
pseudo-elements
are ordered by increasing ordinal
from the nearest element's
contents boundaries.
For example:
Let's take the example of a div element creating two ::before pseudo-elements at ordinals 1 and 4, and three ::after pseudo-elements at ordinals 1, 5 and 8. The rules would look like this:
div::before(1) {} div::before(4) {} div::after(1) {} div::after(5) {} div::after(8) {}The pseudo-elements and the contents of the element are then ordered in this way:
-- element's boundary -- [ ordinal 4, before ] [ ::before, ordinal 1, before ] -- element's contents boundary -- -- element's contents boundary -- [ ::after, ordinal 1, after ] [ ordinal 5, after ] [ ordinal 8, after ] -- element's boundary --
One use of multiple ::before
and ::after
elements
is to mix generated content
with graphical effects.
Pseudo-elements are used
in quotes both
for adding quotation marks
in generated content
OR for drawing a
speech bubble arrow.
With multiple ::before
and ::after
elements
you can get both.
This effect: can be achieved with this code:
div::before { content: "“"; } div::after { content: "”"; } div::after(2) { content: "."; position: absolute; bottom: -10px; right: 20%; border-left: 10px solid transparent; border-right: 10px solid transparent; border-top: 10px solid #111; width: 0; height: 0; } div{ position: relative; float: left; background: #111; padding: 1em; border-radius: 1em; color: white; } <div>I'm talking about CSS Pseudo-elements.</div>
And if you have a single quote
from multiple speakers,
more arrows can be added
by adding more ::after()
pseudo-elements:
In general,
there's no longer a limitation
to two instances
for whatever
a ::before
or ::after
pseudo-element
can be used
(generated content,
graphic effects,
clearfix, etc.).
Another use of multiple
::before
and ::after
pseudo-elements
is to change the style
of pieces of generated content.
If a citation appended to a quote
should be in a different font
than the quotation mark,
two ::after() pseudo-elements
can be used.
blockquote::before { content: "“"; } blockquote::after { content: "”"; } blockquote::after(2) { content: " - " attr(data-author); font: normal 0.8em sans-serif; } <blockquote data-author="Mark Twain">The funniest things are the forbidden.</blockquote>
A use for ordinals
is to allow muliple stylesheets
to add their own ::before
and ::after
pseudo-elements
and coordinate insertion positions.
If one stylesheet uses
a single ::before(10) {}
rule,
then another stylesheet gets
the option to
::before(5) {}
::before(15) {}
Pseudo-elements can be used to generate boxes in CSS for a region chain. Allowing more than two such boxes per element could allow the main CSS Regions example to be written without empty divs for the regions in the markup.
Region chains with a fixed number of regions (usually with an auto-height region at the end of the chain) can be defined entirely in CSS using multiple pseudo-elements.
<style> #grid { width: 80vw; height: 60vw; grid-template: "aaa.d" "....d" "bbb.d" "....d" "ccc.d"; grid-rows: 52% 4% 20% 4% 20%; grid-columns: 30% 5% 30% 5% 30%; } #grid::before(2) { grid-cell: a; } #grid::before { grid-cell: b; } #boxA { grid-cell: c; } #grid::after { grid-cell: d; } #body::after { width: 80vw; } #grid::before { column-count: 2; } article { flow-into: article_flow; } #grid::before(2), #grid::before, #grid::after, #body::after { flow-from: article_flow; } </style> <article> <h1>Introduction</h1> <p>This is an example ...</p> <h2>More Details</h2> <p>This illustrates ...</p> <p>Then, the example ...</p> <p>Finally, this ...</p> </article> <div id="grid"> <div id="boxA"></div> </div>
Several choices of syntax for declaring multiple ::before and ::after pseudo-elements have been considered.
::before[2]
::hypothetical(args)[ordinal]
::before(2)
We arbitrarily chose the order here. Pseudo-elements could be
ordered differently, for example as in:
-- element's boundary -- [ ::before, ordinal 1, before ] [ ordinal 4, before ] -- element's contents boundary -- -- element's contents boundary -- [ ordinal 8, after ] [ ordinal 5, after ] [ ::after, ordinal 1, after ] -- element's boundary --
or
-- element's boundary -- [ ::before, ordinal 1, before ] [ ordinal 4, before ] -- element's contents boundary -- -- element's contents boundary -- [ ::after, ordinal 1, after ] [ ordinal 5, after ] [ ordinal 8, after ] -- element's boundary --
We expect to resolve this issue with the CSS Working Group.
The new ::nth-pseudo()
and ::nth-last-pseudo()
pseudo-elements select pseudo-elements based on indices, not ordinals.
They both take two comma-separated mandatory arguments:
before
,
after
,
letter
,
line
,
column
an+b
syntax or the odd
or even
keywordsNote We may extend the
list of available types in the future for instance to the wrapper
value to create nested pseudo-ancestors of an element.
Let's reuse the example in Example 8 above and let's show the indices:
-- element's boundary -- [ ordinal 4, before, nth index 2, last-nth index 1 ] [ ::before, ordinal 1, before, nth index 1, last-nth index 2 ] -- element's contents boundary -- -- element's contents boundary -- [ ::after, ordinal 1, after, nth index 1, last-nth index 3 ] [ ordinal 5, after, nth index 2, last-nth index 2 ] [ ordinal 8, after, nth index 3, last-nth index 1 ] -- element's boundary --
::nth-pseudo()
and ::nth-last-pseudo()
cannot create a new pseudo-element and the 'content' and 'flow-from'
properties do not apply to them.
They are allowed as the second parameter of ViewCSS.getComputedStyle()
if and only if the index parameter can select only one pseudo-element: for
instance 12
or 0n+5
.
The column
type value allows to select columns
created by element through the CSS Multi-column Layout Module [CSS3COL].
Such columns are pseudo-elements and all these pseudo-elements have an
ordinal equal to their index is the collection of columns created by the
element.
::nth-last-pseudo(column, odd)
will select all
odd columns created by an element, counting from the last one.
Pseudo-elements should be reachable by script, stylable from script, and available as event targets.
Note We may extend this section in the future to allow creation of pseudo-elements from script.
The CSSPseudoElement
interface allows pseudo-elements to be styleable from script and makes them event targets.
interface CSSPseudoElement {
readonly attribute unsigned long ordinal;
// the ordinal of a column is its index
readonly attribute DOMString type;
readonly attribute CSSStyleDeclaration style;
};
CSSPseudoElement implements EventTarget;
The ordinal
attribute represents the ordinal of the pseudo-element for the object's type.
It is a strictly positive integer. The value 1
for the before
(or after
, letter
and line
)
types represents the ::before
(or ::after
,
::first-letter
and ::first-line
)
pseudo-elements.
The type
attribute is a string representing the type of the pseudo-element. This can be one
of the following values:
ordinal
is the index of column in the collection of columns created by the
element.The style
attribute is a CSSStyleDeclaration
[CSSOM] allowing
to set directly style information (inline styles) onto the pseudo-element.
Inline styles on a CSSPseudoElement
have precedence over all
style rules styling that pseudo-element.
The EventTarget
interface [DOM-LEVEL2-EVENTS] must be
implemented by all instances of CSSPseudoElement
in an in
implementation which supports the current specification.
The CSSPseudoElementList
represents an ordered collection
of CSSPseudoElement
instances.
interface CSSPseudoElementList {
readonly attribute unsigned long length;
CSSPseudoElement item(unsigned long index);
CSSPseudoElement getByOrdinalAndType(unsigned long ordinal,
DOMString type);
// replies null if no pseudo-element does not exist for
// the requested ordinal and type
};
The length
attribute represents the number of CSSPseudoElement
in the
collection or zero if it is empty.
The method item()
is used to retrieve a CSSPseudo
Element
by
index. It takes one parameter being the requested index into the
collection. Its return value is the CSSPseudo
Element
at the requested index in the collection or null if that is not a valid
index.
The method getByOrdinalAndType()
is used to retrieve a CSSPseudo
Element
by its
ordinal and type. It takes two parameters: first, the requested
ordinal; second a type. Its return value is the CSSPseudo
Element
at the requested index in the collection or null if there is no CSSPseudo
Element
in the collection for that index and type.
A new method is added to the Window
interface to retrieve
pseudo-elements created by a given element for a given type:
partial interface Window {
CSSPseudoElementList getPseudoElements(Element elt,
DOMString type);
};
The method getPseudoElements()
is used to retrieve all CSSPseudoElement
instances created
by the element elt
for the type type
.
Its return value is a CSSPseudoElementList
, potentially
empty if no pseudo-element exists for the given element and the given
type.
The editors would like to thank the following individuals for their contributions, either during the conception of the specification or during its development and specification review process:
Tab Atkins, Razvan Caliman, Chris Coyier, Anders Grimsrud, Vincent Hardy.