CSS railroad diagrams
So next step after the sexy CSS lexer is parsing. But first - railroad diagrams to help visualize how/when the tokens make sense forming valid CSS code.
Below is what I have so far. It includes pretty much everything except selectors. Selectors are getting increasingly complex, come to think of it.
There are probably mistakes in these diagrams, so I'll be happy to see any cases where they don't work properly.
Here's the list of png images and the powerpoint file I used to make them, in case you want to use (or correct) the diagrams for your own purposes.
Stylesheet

A stylesheet can have an optional charset, followed by zero or more imports, then zero or more namespace definitions. I struggled a bit whether I want my parser to support namespaces, because I don't think they are widely used, nor exceptionally helpful, but webkit supports them since quite a while, so, heck, let there be namespaces, although they make everything more complex. No wonder JavaScript still doesn't have (or plans for) namespaces.
So after these intro @-rules, there could be any number or combination of rulesets, @font-face, @page, @media and @keyframes, in any order.
@charset

Pretty simple, just the literal @charset followed by a string. It has to be the first thing in the CSS file and there can only be one.
E.g.
@charset "UTF-8";
@import

Any number of imports follow the charset, they can define the URL of the imported CSS as a string or using url(). There's an optional media identifier (in CSS2) turned media query (CSS3). In other words these are all valid:
@import "stuff.css"; @import url(stuff.css); @import url(stuff.css) print, handheld; @import url(stuff.css) screen and (color); @import "stuff.css" screen and (color), projection and (min-color: 256);
Ah, media query syntax, lovely.
@namespace

Namespaces (arrgh!) are innocent-looking to declare:
@namespace svg "http://www.w3.org/2000/svg"; @namespace svg url(http://www.w3.org/2000/svg); @namespace "http://example.org"; @namespace empty ""; @namespace "";
@keyframe

Horray for more @-rules. This one is pretty cool actually, has been working well in WebKit (I've used it) via @-webkit-keyframe and allows us to take serious amounts of animation-related code out of JS.
@keyframes 'diagonal-slide' { from { left: 0; top: 0; } to { left: 100px; top: 100px; } }
@media

We've all used stuff like @media print {.banner {display: none}} for quite a while, but things are getting complex these days with the media queries and the ability to nest @page blocks (which can have blocks of their own). Hairier and hairier.
@media screen { margin: 20px; } @media print and (color), projection and (device-aspect-ratio: 16/9) { @page :left { @bottom-left-corner { margin: 2cm; } } }
Media query

Media queries can get a little scary. In their simplest form they can just be the media type. One of:
aural braille handheld print projection screen tty tv embossed speech all
(The fact that "all" is also optional makes me think there's a problem with this specific diagram but makes my head hurt. And nose bleed.)
So, simple:
@media print {...}
Or:
@media print, screen, handheld {...}
But you can be more specific, using AND and providing more details about the media type using the so-called media expressions.
@media handheld AND (orientation: portrait) {...}
Then you can negate the whole query with NOT and hide from old browsers with ONLY.
In fact this is pretty popular way to style for iPhone:
@media only screen and (max-device-width: 480px) { #bigbanner { display: none; } }
You can also keep adding AND expressions and be really picky about the type of device and the features it supports
Media expression

The media expressions (not to be confused with IE's expression() value) are wrapped in parentheses and can be:
- just the feature, e.g.
(color) - feature plus specific value for it, e.g.
(orientation: portrait) - min or max value for a feature, e.g.
(min-width: 100px). When there's min/max prefix, the value is required
The feature can be one of:
width height device-width device-height orientation aspect-ratio device-aspect-ratio color color-index monochrome resolution scan grid
Those in italics don't support min- and max- prefixes
Ruleset

Whew, finally something that looks normal - the old:
#mydiv { color: red }
Of course, selectors are complicated, I'll deal with them in a later post.
I've moved the property: value declarations into something I called "block" since it occurs not only in rulesets.
Block

Any number of property: value pairs (a.k.a. declarations), delimited by a ; and wrapped in curly braces.
Declaration

The plain old property: value pairs.
What the allowed values are, depends on the property. And what the property is, depends on the type of block. Blocks will be reused, for example in @keyframe and @font-face where for example there's a src property, which doesn't make sense in a normal #mydiv {} block.
You can see that I've decided my parser to allow !important as well as !ie. Since IE treats anything after ! as if it was !important, this is a common hack to make something important for IE only. This is the type of thing that I'd like to have that allows real-life validation. And why !ie and not just random !noodles? Well, !ie hints the intent and is more maintainable, where allowing anything including !importent (probably a mistype) is not too validaty and helpful.
@font-face

The @font-face. Nothing overly exciting actually.
@page and page blocks


This one can become complex too, but heck, it allows us to style content for book publishing! In a worst case scenario it could have three levels of nested blocks.
The thing is that inside of a normal property: value block there can be nested ruleset-like constructs (only instead of selector, there's a page margin)
@media print { @page mine :left { margin: 1cm; @bottom-left-corner { margin: 2cm; } } } /* or something simpler */ @page { size: A4 landscape; }
The page margins allowed are:
@top-left-corner @top-left @top-center @top-right @top-right-corner @bottom-left-corner @bottom-left @bottom-center @bottom-right @bottom-right-corner @left-top @left-middle @right-bottom @right-top @right-middle @right-bottom
The pseudo page values allowed are
:left :right :first
Thanks
Did you just scrolled here or you read it all?
Thanks for reading and looking forward to any mistake pointers!
This entry was posted on Sunday, November 28th, 2010 and is filed under CSS. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.
Get notification for future posts: follow me on Twitter or subscribe to my RSS feed

November 28th, 2010 at 3:31 am
[...] This post was mentioned on Twitter by Vladimir Carrer and Stoyan Stefanov, Johannes J. Schmidt. Johannes J. Schmidt said: RT @stoyanstefanov: Blog: CSS railroad diagrams http://www.phpied.com/css-railroad-diagrams/ [...]
November 28th, 2010 at 4:12 am
[...] more here: CSS railroad diagrams / Stoyan's phpied.com Related Posts:CSS Lexer / Stoyan's phpied.com The test page takes CSS , tokenizes it, then [...]
November 29th, 2010 at 3:37 am
Hi Stoyan,
This is really a great research. Very cool thing to read! Actually this blog is one of my favorites because of this scientific approach!
Thanks!
November 29th, 2010 at 6:52 am
Read it all. I actually found some stuff that i did not know about before. Interesting article.
Good luck.
November 29th, 2010 at 3:22 pm
“ince IE treats anything after ! as if it was !important”
Are you sure about this one? I have been unable to produce it in IE7 or IE8.
November 30th, 2010 at 1:58 am
@Taimar, here’s some more:
http://en.wikipedia.org/wiki/CSS_filter#.21important_quirks
@stoimen, thanks man!
November 30th, 2010 at 11:02 am
[...] CSS railroad diagrams at Stoyan's phpied.com (tagged: CSS webdev ) [...]
December 4th, 2010 at 5:44 pm
Thanks a lot buddy.
December 6th, 2010 at 6:07 pm
[...] Post about parsing, with railroad diagrams: http://www.phpied.com/css-railroad-diagrams/ [...]
December 13th, 2010 at 5:52 am
I see the idea for a bit more robust CSS parsing in YUI Compressor is still in both of our heads,
.
I assume you’ve already been using this a bit already, but http://www.w3.org/TR/2007/CR-CSS21-20070719/grammar.html is your friend. It’s funny to see remnants like (CDO/CDC, to protect your browser from that new CSS thing!) and interesting things I hadn’t seen before (like “=~” and “|=”). Also, a lot of ruleset: expression; units were new to me (ex, Hz, KHz, deg, rad, grad, ms [millseconds], oh my!).
Hoping to mess around with something like this very soon as well.
P.S. Will you be minifying the JScript inside of MSIE’s expression()s? (*shudders thinking about this, haha*)
December 16th, 2010 at 12:29 pm
What are you planning to use it for?
December 17th, 2010 at 6:58 pm
i think that the @media diagram might be incomplete.
the @media has rulesets which are inside {}, similar to your @keyframe diagram.
I am pointing it out because most minifiers have problmes parsing media queries.
For example, both AJAXmin and CSStidy break the CSS. The YUI CSS compressor seems good.
December 22nd, 2010 at 6:42 am
Interesting post, keep up the good work!
December 23rd, 2010 at 1:19 am
[...] I read a really great book during the November holiday weekend about object-oriented JavaScript. What do you know, it’s actually called “Object-Oriented JavaScript” and it’s by Stoyan Stefanov. [...]
December 27th, 2010 at 10:55 am
The CSS specs provide all of the parsing rules in LEXX format, so no need to reinvent the wheel. For example, @import rules may appear after any other rule, although they are interpretted to be at the beginning of the style sheet.
October 21st, 2012 at 10:08 pm
[...] http://www.phpied.com/css-railroad-diagrams/ [...]