CSS is a versatile style language that is most frequently used to control the
look and formatting of an HTML document based on information in the document
tree. But there are some common publishing effects – such as formatting the
first line of a paragraph – that would not be possible if you were only able
to style elements based on this information. Fortunately, CSS has
pseudo-elements and pseudo-classes.
As their names imply, they are not part of the DOM in the way that ‘real’ HTML
elements and classes are. Instead, they are CSS abstractions that provide
additional, and otherwise inaccessible, information about the document.
This article will discuss the CSS pseudo-elements that are part of CSS 2.1 –
:first-letter
, :first-line
, :before
, and :after
– and how the :before
and :after
pseudo-elements can be exploited to create some interesting
effects, without compromising the simplicity of your HTML. But first, let’s
look at each type of pseudo-element and how to use them in their basic form.
The :first-line
and :first-letter
pseudo-elements
The :first-line
pseudo-element lets you apply styles to the first formatted
line of a block container element (i.e., elements with their display
property
set to block
, inline-block
, list-item
, table-caption
, or table-cell
).
For example:
p:first-line { font-weight: bold; }
…will change the first line of every paragraph to bold. The :first-line
pseudo-element can be treated as if it were an extra HTML inline element
wrapping only the first line of text in the paragraph.
The :first-letter
pseudo-element lets you apply styles to the first letter
(and any preceding punctuation) of the first formatted line of a block
container element. No other inline content (e.g. an image) can appear before
the text. For example:
p:first-letter { float: left; font-size: 200%; }
…will produce a basic ‘drop cap’ effect. The first letter of every paragraph
will be floated left, and twice as large as the other letters in the paragraph.
The :first-letter
pseudo-element can be treated as if it were an extra HTML
inline element wrapping only the first letter of text in the paragraph.
The :first-line
and :first-letter
pseudo-elements can only be attached to
block container elements, but the first formatted line can be contained within
any block-level descendant (e.g., elements with their display
property set to
block
or list-item
) in the same flow (i.e., not floated or positioned). For
example, the following HTML fragment and CSS:
<div><p>An example of the first line of text being within a descendant element</p></div>
div:first-line { font-weight: bold; }
…would still result in a bold first line of text, because the paragraph’s text
is the first formatted line of the div
.
The :before
and :after
pseudo-elements
The :before
and :after
pseudo-elements are used to insert generated content
before or after an element’s content. They can be treated as if they were
extra HTML inline elements inserted just before and after the content of their
associated element.
Generated content is specified using the content
property which, in CSS 2.1,
can only be used in conjunction with the :before
and :after
pseudo-elements. Furthermore, you must declare the content
property in order
to generate the :before
and :after
pseudo-elements.
The content
property can take string, url()
, attr()
, counter()
and
counters()
values. The url()
value is used to insert an image. The attr()
function returns as a string the value of the specified attribute for the
associated element. The counter()
and counters()
functions can be used to
display the value of any CSS counters.
For example, the following HTML fragment and CSS:
<a href="http://wikipedia.org">Wikipedia</a>
a:after { content: " (" attr(href) ")"; }
…would display the value of the href
attribute after a link’s content,
resulting in the following anchor text for the example above: Wikipedia
(http://wikipedia.org)
. This can be a helpful way to display the destination of
specific links in printed web documents.
Keep in mind that CSS is meant for adding presentation and not content.
Therefore, the content
property should be used with caution.
It’s also worth noting that the :first-letter
and :first-line
pseudo-elements apply to the first letter and first line of an element
including any generated content inserted using the :before
and :after
pseudo-elements.
Browser support for pseudo-elements
The :first-letter
and :first-line
pseudo-elements were introduced in CSS1
and there is wide basic support for them. However, IE 6 and IE 7 have
particularly buggy implementations; even modern browsers are not entirely
consistent in the way that they handle the :first-line
and :first-letter
pseudo-elements (example bugs).
The :before
and :after
pseudo-elements were introduced in the CSS 2.1
specification and are fully implemented in Firefox 3.5+, IE 8+, Safari 3+,
Google Chrome, and Opera. Modern versions of Firefox even support CSS
transitions and animations applied to pseudo-elements. However, legacy browsers
like IE 6 and IE 7 do not support the :before
and :after
pseudo-elements at
all.
For more detailed information on pseudo-element browser support, browser bugs,
and workarounds, have a look at Sitepoint’s
reference and this article
on IE 6/7 issues.
In most cases, the :before
and :after
pseudo-elements can be used as part
of a ‘progressive enhancement’ approach to design and development, because IE 6
and IE 7 will simply ignore them altogether. Alternatively,
Modernizr now includes a robust feature test for
generated content, providing one way to specify fallbacks or enhancements
depending on browser support. The important thing is to remember to check what
happens in browsers where support is missing.
Alternative ways to use pseudo-elements
Let’s take a look at how the :before
and :after
pseudo-elements can be used
as the basis for some interesting effects. Most of the time, this involves
generating empty :before
and :after
pseudo-elements by declaring an empty
string as the value of the content
property. They can then be manipulated as
if they were empty inline HTML elements, keeping your HTML clean and giving you
full control of certain effects from within CSS style sheets.
Simple visual enhancements, like speech bubbles and folded corners, can even be
created without the need for images. This relies on the fact that you can
create simple shapes using CSS.
Several types of ‘CSS polygons’ can be created as a result of browsers
rendering borders at an angle when they meet. This can be exploited to create
triangles. For example, the following HTML fragment and CSS:
<div class="triangle"></div>
.triangle {
width: 0;
height: 0;
border-width: 20px;
border-style: solid;
border-color: red transparent transparent;
}
…will create a downward pointing, red triangle. By varying the width, height,
border-width, border-style, and border-color values you can produce different
shapes and control their orientation and colour. For more information, be sure
to read Jon Rogan’s summary of the
technique.
The more advanced pseudo-element hacks use the extra background canvas afforded
by each :before
and :after
pseudo-element. This can help you crop
background images, control the opacity of background images, and ‘fake’
multiple backgrounds and
borders in browsers without
support for CSS3 multiple backgrounds (e.g., IE 8). Taken to ludicrous
extremes, you can even build a whole CSS icon set. To
start with, let’s look at some simple effects that can be created without
images or presentational HTML.
Creating CSS speech bubbles
In this example, a quote is styled to look like a speech bubble, using
CSS. This is done by creating a triangle using a
pseudo-element, and then absolutely positioning it in the desired place. By
adding position:relative
to the CSS styles for the HTML element, you can
absolutely position the :after
pseudo-element relative to its associated
element.
<div class="quote">[Quoted text]</div>
.quote {
position: relative;
width: 300px;
padding: 15px 25px 20px;
margin: 20px auto;
font: italic 26px/1.4 Georgia, serif;
color: #fff;
background: #245991;
}
.quote:after {
content: "";
position: absolute;
top: 100%;
right: 25px;
border-width: 30px 30px 0 0;
border-style: solid;
border-color: #245991 transparent;
}
There’s nothing stopping you from adding some CSS3 to further enhance the
effect for capable browsers. This could be adding rounded corners to the box or
applying a skew transform to the triangle itself. Fiddle with the code in this
example.
Creating CSS ‘ribbons’
Using the same principle, you can create a CSS ribbon effect without images or
extra HTML. This time the effect uses 2 pseudo-element triangles. The HTML
fragment is still very simple.
<div class="container">
<h1>Simple CSS ribbon</h1>
<p>[other content]</p>
</div>
You then need to use negative margins to pull the h1
outwards so that it
extends over the padding and beyond the boundaries of the container div
. The
HTML fragment above can be styled using the following CSS:
.container {
width: 400px;
padding: 20px;
margin: 20px auto;
background: #fff;
}
.container h1 {
position: relative;
padding: 10px 30px;
margin: 0 -30px 20px;
font-size: 20px;
line-height: 24px;
font-weight: bold;
color: #fff;
background: #87A800;
}
From here, you only need to add the pseudo-element triangles to create the
‘wrapping’ appearance associated with ribbons. The :before
and :after
pseudo-elements share many styles, so you can simplify the code by only
overriding the styles that differ between the two. In this case, the triangle
created with the :after
pseudo-element must appear on the opposite side of
the heading, and will be a mirror image of the other triangle. So you need to
override the shared styles that control its position and orientation.
.container h1:before,
.container h1:after {
content: "";
position: absolute;
top: 100%;
left: 0;
border-width: 0 10px 10px 0;
border-style: solid;
border-color: transparent #647D01;
}
.container h1:after {
left: auto;
right: 0;
border-width: 0 0 10px 10px;
}
Fiddle with the code in this example.
Creating CSS folded corners
The final example of this form of pseudo-element hack creates a simple CSS
folded-corner effect. A pseudo-element’s
border
properties are set to produce two differently-coloured touching
triangles. One triangle is a slightly darker or lighter shade of the box’s
background colour. The other triangle matches the background colour of the
box’s parent (e.g. white). The pseudo-element is then positioned in the top
right corner of its associated element to complete the effect.
.note {
position: relative;
padding: 20px;
margin: 2em 0;
color: #fff;
background: #97C02F;
}
.note:before {
content: "";
position: absolute;
top: 0;
right: 0;
border-width: 0 16px 16px 0;
border-style: solid;
border-color: #658E15 #fff;
}
Varying the size of the borders will vary the size and angle of the
folded-corner. Fiddle with the code in this
example.
Pseudo background-crop
Although creating polygons with pseudo-elements can produce some popular
effects without images, the possibilities are inherently limited. But this is
only one type of :before
and :after
pseudo-element hack. Treated as extra
background canvases, they can be used to fill some gaps in existing browser
support for CSS features.
One of those features is the cropping of background images. In the future, it’s
likely that you’ll be able to crop background images using fragment
identifiers, as is proposed in the CSS Image Values Module Level 3 draft. But
at the moment no browsers support the use of fragment identifiers with bitmap
images. Until they do, you can make use of this CSS 2.1 hack to emulate
background image cropping in modern browsers.
The principle behind a ‘pseudo
background-crop‘ is to apply a
background-image to a pseudo-element rather than directly to an element in the
HTML document. One of the applications of this technique is to crop icons that
are part of a sprite.
For example, a web app might allow users to ‘save’, ‘edit’, or ‘delete’ an
item. The HTML involved might look something like this:
<ul class="actions">
<li class="save"><a href="#">Save</a></li>
<li class="edit"><a href="#">Edit</a></li>
<li class="delete"><a href="#">Delete</a></li>
</ul>
To enhance the appearance of these ‘action’ links, it is common to see icons
sitting alongside the anchor text. For argument’s sake, let’s say that the
relevant icons are part of a sprite that is organised using a 16px × 16px grid.
The :before
pseudo-element – with dimensions that match the sprite’s grid
unit – can be used to crop and display each icon. The sprite is referenced as a
background image and the background-position
property is used to control the
precise positioning of each icon to be shown.
.actions a:before {
content: "";
float: left;
width: 16px;
height: 16px;
margin: 0 5px 0 0;
background: url(sprite.png);
}
.save a:before { background-position: 0 0; }
.edit a:before { background-position: -16px 0; }
.delete a:before { background-position: -32px 0; }
Using pseudo-elements like this helps to avoid the need to either add liberal
amounts of white space to sprites or use empty HTML elements to do the
cropping. Fiddle with the code in this
example.
Pseudo background-position
The CSS 2.1 specification limits the values of background-position
to
horizontal and vertical offsets from the top-left corner of an element. The CSS
Backgrounds and Borders Module Level 3 working draft includes an improvement to
the background-position
property to allow offsets to be set from any side.
However, Opera 11+ is currently the only browser to have implemented it.
But by using pseudo-elements, it’s possible to emulate positioning a background
image from any side in any browser with adequate CSS 2.1 support –’pseudo
background-position‘.
Once a pseudo-element is created, it must be absolutely positioned in front of
the associated element’s background but behind its content, so as not to
prevent users from being able to select text or click on links. This is done by
setting a positive z-index
on the element and a negative z-index
on the
pseudo-element.
#content {
position: relative;
z-index: 1;
}
#content:before {
content: "";
position: absolute;
z-index: -1;
}
Now the pseudo-element can be sized and positioned to sit over any area within
(or beyond) the element itself, without affecting its content. This is achieved
by using any combination of values for the top
, right
, bottom
, and left
positional offsets, as well as the width
, and height
properties. It is the
key to their flexibility.
In this example, a 200px × 300px background image is applied to the
pseudo-element, which is also given dimensions that match those of the image.
Since the pseudo-element is absolutely positioned, it can be offset from the
bottom and right of the associated HTML element.
#content {
position: relative;
z-index: 1;
}
#content:before {
content: "";
position: absolute;
z-index: -1;
bottom: 10px;
right: 10px;
width: 200px;
height: 300px;
background: url(image.jpg);
}
Many other hacks and effects are possible using the :before
and :after
pseudo-elements, especially when combined with CSS3. Hopefully this
introduction to pseudo-elements, and how they can be exploited, will have
inspired you to experiment with them in your work.
The future of pseudo-elements
The way that pseudo-elements are used will continue to change as CSS does. Some
new applications will emerge, and existing ones will fade away as browser
implementation of ‘CSS3 modules’ continues to improve.
Generated content and pseudo-elements themselves are likely to undergo changes
too. The CSS3 Generated and Replaced Content Module introduced a two-colon
format for pseudo-elements (i.e., ::before
) to help distinguish between
pseudo-classes and pseudo-elements. But for compatibility with previous levels
of CSS, pseudo-elements do not require two colons. Most modern browsers support
both formats, but it is not supported by IE 8 and the single-colon format
ensures greater backwards compatibility.
The proposed extensions to pseudo-elements included the addition of nested
pseudo-elements (::before::before
), multiple pseudo-elements (::after(2)
),
wrapping pseudo-elements (::outside
), and the ability to insert
pseudo-elements into later parts of the document (::alternate
). However, the
CSS3 Generated and Replaced Content Module is undergoing significant changes.
This article was originally published in .net magazine in April 2011