Fun with generated content (:before, :after)

Hello Everybody,

I recently published ReMarkdown.css, a reusable stylesheets which displays common HTML elements as if they were plain Markdown text. It was a nice experiment to work on and I’d like to share a few snippets of code that show off how it uses generated content.

First, for reference, generated content is defined in CSS 2.1 - Generated content, automatic numbering, and lists. By using :before and :after in your selectors, along with a content property with some value other than normal or none (including the empty string: “”), you generate a pseudo-element that can accept text content and be styled like you would style a SPAN element. Note that generated content is supported in all modern browsers, including IE8, but not in IE7.

Some uses of generated content are for pure decoration tricks. For instance with this code you can use three different backgrounds in IE8 (which doesn’t support CSS3 multiple backgrounds):

/* Random example */
#container {
  background: #FFFFFF url(some-image) repeat-y center top;
  display: block; /* default */
  position: relative;
  width: auto; /* default */
}
#container:before {
  background: url(some-other-image) no-repeat left top;
  content: "";
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
}
#container:after {
  background: url(some-other-image) no-repeat right bottom;
  content: "";
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
}

In ReMarkdown.css I’m using generated content to add text characters that mimick the syntax of Markdown. For instance, here’s how you can display a link as link text:

a[href]:before {
  content: "[";
}
a[href]:after {
  content: "](" attr(href) ")";
}

In CSS 2.1 the attr() syntax allows you to retrieve the contents of any of the element’s attributes. In CSS 3 there are some enhancements for retrieving content from different elements in the document, but I haven’t experimented with those yet.

Remember that you can use character escapes in CSS, which might prove useful sometimes. For instance, I wanted to add > signs to the left of each line of BLOCKQUOTE elements. Since there are no CSS selectors for individual lines (I wish I could just write blockquote::nth-line(1n)::before {content: “>”} with some CSS3 magic, but there is no nth-line pseudo-element), I had to add one pseudo-element with a vertical series of > characters. This is the trick I used:

.rmd-on blockquote {
  margin: 1em 0;
  overflow: hidden;
  padding-left: 3ch; /* CSS3 unit, means "character width" */
  position: relative;
}
.rmd-on blockquote:before {
  content: ">\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A"
  ">\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A"
  ">\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A"
  ">\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A";
  position: absolute;
  top: 0; left: 0;
  white-space: pre;
}

The “\A” sequence maps to unicode U+000A (LINE FEED). So it’s basically a crazy long string of > characters separated by newlines, absolutely positioned. Everything that overflows the BLOCKQUOTE element is hidden, so if your quote spans 3 lines, you see only 3 “>” characters (provided you manage the line heights right).

There’s a similar trick to add the “=====” or “-----” underlines on H1 and H2 titles, while adding the “#” signs to the left of H3-H6 titles is a more classic, dead-simple use of generated content:

h3:before {
  content: "### ";
}
h4:before {
  content: "#### ";
}

I’ll stop there and invite you to take a look at the remarkdown.css stylesheet for more.

A few notes on generated content

  • Support in current modern browsers is good (at least for the CSS 2.1 parts). IE7 and ealier had no support altogether. Older versions of Firefox or Safari (FF2 or maybe 3.0, Safari 3 or 4) supported generated content but had some issues with formatting or positioning the :before and :after pseudo-elements; looks like the few bugs they add have been fixed in the past few years.
  • Don’t use it for adding real content to your pages. CSS-generated content will not be reflected to screen readers (or unreliably so), and of course not to search engines either.

PS: if moderators think this post runs afoul of the Self promotion/self-linking rule, I’ll be glad to remove the link to my project.

In CSS 2.1 the attr() syntax allows you to retrieve the contents of any of the element’s attributes. In CSS 3 there are some enhancements for retrieving content from different elements in the document, but I haven’t experimented with those yet.

I imagine you’re referring to the move-to property and pending() function. To my understanding, they’re long overdue to be dropped from the CSS3 generated content module in favor of XBL2. They’re also not implemented anywhere.

I was thinking of this note in the CSS 2.1 spec:

Note. In CSS 2.1, it is not possible to refer to attribute values for other elements than the subject of the selector.

I took a look at the CSS 3 module for generated content, but it’s a Working Draft from 2003, and has holes in possibly relevant sections (I was thinking of the string-set property and string() function). So I didn’t even try to use it, and focused on CSS 2.1.

I’ve always found these icons made with CSS to be a good example of CSS generated content: http://nicolasgallagher.com/pure-css-gui-icons/

In CSS 3 there are some enhancements for retrieving content from different elements in the document, but I haven’t experimented with those yet.

How can you retrieve “content” from elements with CSS3, or did you mean content from attributes?

You can store the text content of an element in an identifier with the [I]string-set[/I] property, and retrieve it in the value of a content property with the string() function. But this is only the theory and it was never fully specced. So basically you can’t do more with CSS3 than with CSS 2.1 regarding generated content, since the CSS3 module is an almost abandoned Working Draft and nothing from it will be implemented in browsers if it doesn’t get some love, and a perhaps even a Candidate Recommendation or Last Call status.

I think this is the best consolidated use of the css and even we can do more more wonders with css file inorder to make our content look more blemish and attractive…

thanks for this.

In the past, people had hardcoded inline style in their HTML.
Then, someone came up to separate content and style so style would be into external .css files.
And now, we try to add content via external stylesheet files :slight_smile:

That’s disappointing. Actually I was hoping there was a way to use the url() attribute with a retrieved string.

for example:
<p> see <a href=“images/img.jpg”>this image</a></p>
a:after{ content:url(attr(href)); height:2em}

would have been handy… W/O really adding content per say… but I can see how thats opening a can of worms in many directions too.

The reason the gencontent spec isn’t getting love is because anything relatively dynamic that could be included in it is already achievable with XBL2 (in a much more well thought-out way I might add). Now on the flipside, if XBL2 doesn’t get some love with regards to implementations, it’s in trouble.