Whitespace and generated content

1 June 2005

CSS is fun and I'm still learning it because in my opinion, it's a never-ending process. Like web pages, CSS is a growing entity. There are always more things to learn and understand. This time, I focus on CSS whitespace and generated content.

I first got interested in CSS whitespace handling when I saw these lines of code from Ian Hickson's site stylesheet, about two years ago till now:

This looks confusing. Therefore, I asked Ian about this intricacy. He explained, in CSS2, there is no way to indicate that spaces and newlines should be preserved, but that if the text reaches the end of the containing block, it is okay to wrap it. The closest is white-space: pre, but that doesn't wrap. Before CSS2.1 is in Candidate Recommendation, user agents are not allowed to implement them, so they have implemented proprietary extensions. Ian lists all these possibilities, and because of CSS's forward compatibility guidelines, user agents pick the last one they support.

As for the last property, word-wrap: break-word is Internet Explorer's proprietary extension which is not part of any standards and will not be described here.

What is the difference between pre, pre-wrap and pre-line? How about normal and nowrap then? The more I read on the white-space property, the more I get confused with the meanings of the values and its processing model. Again, Ian helped me to simplify things up with a quick and basic table that shows the naming convention and relationships among the values:

Relationships for values of the white-space property

name

spaces

wrapping

newlines

normal

collapse

wrap

ignore

pre-line

collapse

wrap

preserve

nowrap

collapse

don't

ignore

(none)

collapse

don't

preserve

(none)

preserve

wrap

ignore

pre-wrap

preserve

wrap

preserve

(none)

preserve

don't

ignore

pre

preserve

don't

preserve

To make things clear, pre-wrap acts like pre but wraps if necessary, while pre-line acts like normal but preserves newlines. I'm sure pre-wrap would be very useful for displaying long lines of computer codes that might overlap on other elements or goes off the screen. From the table, there are some which don't have names yet, due to repeatedly failed attempts to come up with better names. However in CSS3, each of the facets of these values can be individually controlled, as documented in the CSS3 Text Module under line breaking and text wrapping.

Okay, how about browser support? Most modern browsers now correctly support pre, normal and nowrap. Firefox supports -moz-pre-wrap but not pre-wrap and pre-line yet, reported as Bug 261081 and Bug 230555. Opera 8 supports pre-wrap including its previous extensions, -pre-wrap and -o-pre-wrap, but not pre-line. I guess, pre-line is much harder to be implemented?

Now, let's take this one step further. I start to fiddle with CSS content generation, specifically using the content property with the :before and :after pseudo-elements. Here are some of my experiments, starting with basic HTML codes:

<div title="some
title
text">text inside container</div>

Note that there are two line breaks, purposedly typed, in the value of the title attribute. Accompanied with a little CSS:

Of course, this is not an intended effect because the newlines escaped with a backslash will be ignored in the rendering. I read the specification again and found a way to include line breaks or newlines in strings:

To include a newline in a string, use an escape representing the line feed character in Unicode (U+000A), such as "\A" or "\00000a". This character represents the generic notion of "newline" in CSS.

Opera 8 renders the line feed characters but not Firefox. At first, I thought Firefox couldn't read this character yet but later, I found out that:

Authors may include newlines in the generated content by writing the "\A" escape sequence in one of the strings after the 'content' property. This inserted line break is still subject to the 'white-space' property.

Initially, before white-space: pre is applied, Firefox ignores the escaped line feed character because the :after pseudo-element inherits the white-space: normal style from the p tag. In another case, Opera 8 renders the escaped line feed character, even without white-space: pre, thus proves that the :after pseudo-element is actually applied with white-space: pre-line. Then I see, Opera 8 does support pre-line but only for generated content of pseudo-elements? Is this a wrong implementation?

Interesting. The pre-line behaviour is expected in generated content, instead of inherited? If then, this means Opera 8 is correct. Partially correct, because it still renders the escaped newline under white-space: normal though:

My experiments are done with the help of only two browsers, Mozilla Firefox 1.0+ and Opera 8, on Windows XP. I supposed anything rendered on Firefox should be the same with any Gecko-powered browsers such as Mozilla Suite and Netscape. Obviously, any versions of Internet Explorer, hopefully not 7 and above, are useless and fail all test cases here. Though I might be curious how my experiments would affect Safari and any other standards-compliant browsers.

I'm not sure if this has been discussed somewhere else and I could have missed some points, whatever. So, please correct me if I'm wrong. Overall, CSS is fun, right?

Update 4 June 2005: To make things more visually stimulating, I've prepared a testcase page which includes the above codes, for testing purposes.