CSS Content Generation

So, with all the excitement over the W3C CSS 2.1 spec being finalised just a few months ago (a mere 13 years after the advent of CSS2!), I recently decided to have a quick skim over it. In my defence, I was bored and the spec isn’t as long or dry as specs usually are… with the exception of the section which distinguishes block-level elements, block-level boxes, block container boxes and block boxes 😉

While reading it, I stumbled across something I knew very little about… CSS content generation. The idea is that you can use CSS to insert content into your document like this:

.greetings {
    counter-reset: personCount;

.greetings .greeting {
    counter-increment: personCount;

.greetings .greeting:before {
    content: "Hi " attr(name) "! ";

.greetings .greeting:after {
    content: counter(personCount) ". ";
<div class="greetings">
  <span class="greeting" name="Bob Loblaw" >You are #</span>
  <span class="greeting" name="Tobias Fünke" >You are #</span>

The above CSS uses content attributes which are fed by raw strings, HTML attribute values and CSS counters in order to render something like the following:
Hi Bob Loblaw! You are #1. Hi Tobias Fünke! You are #2.

Now I’m sure that many consider these features to be evil, as the entire purpose of CSS is to separate content from presentation, right? Well, I’d argue that their use is entirely justified in at least a couple of cases:

  • When you have no control over the rendering of some component in your webpage and you want to inject some content without resorting to Javascript. I’ve had to use this on more than one occasion, while taming the beast that is SharePoint.
  • When the content you’re inserting is presentational, or highly structured but trivial. Inserting chevrons before elements is one example. Another is using nested counters to prepend 1.1, 1.2… to items inside hierarchical lists.

Food for thought!

This entry was posted in Web and tagged , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *