ABSTRACT
-----------------------------------------------------------------
Writing CSS is hard, let's use machines to do some of the mundane
tasks.
HTML and CSS are very loosly coupled. This is great, but it also
means that we can't use the HTML to verify the CSS and we also
can't use the CSS to verify the HTML.
Using a separate machine readable specification can allow us to
verify the CSS and the HTML. It can also assist us during any
stage of a project's lifetime.
Another problem are existing sites which accumulated lots of
cruft over the years. That's another thing I'm trying to tackle
here.
TERMINOLOGY
-----------------------------------------------------------------
0. Tree, root, node, leaf, and so on:
http://en.wikipedia.org/wiki/Tree_data_structure
1. Sub tree
Similar to OOCSS's "module" - just completely rigid.
2. Attribute classes
Classes used child nodes. May overlap with other child classes
from other sub trees.
3. Skin classes
Classes used to customize the look of a whole sub tree or its
children.
4. Extending
Reusing a previously defined sub tree. New children can be
added, the order/quantifiers can be changed.
KEY CONCEPTS
-----------------------------------------------------------------
0. Everything is a sub tree.
1. Each sub tree has a root node, which can be a tag, an id, or a
class.
2. Each sub tree root can have 0-n children, which in turn can
also have 0-n children. If a node has no children, it's a
leaf - even if it's a sub tree root. A leaf can contain
arbitrary content (e.g. another sub tree, RTE stuff, etc.).
3. On the CSS side this rigid hierarchical relationship is
mirrored with selectors which utilize the child combinator (>)
exclusively.
4. This means IE6 isn't supported.
5. What we gain is completely predictable behavior. You can put
any sub tree into the leaf node of any other sub tree.
6. Each sub tree is on its own. This means linear complexity.
THE THINGS YOU GAIN
-----------------------------------------------------------------
0. The Spec Itself
a) Provides a clutter free architectural overview.
b) Serves as some kind of minimal documentation of the used
structures.
1. Spec Parsing
a) Check syntax (duh).
b) Ensure that roots are only used once.
c) Check if "attribute" classes collide with roots.
d) Check if "skin" classes collide with roots.
e) Check if "skin" classes collide with "attribute" classes.
f) When extending and adding a child, all previously defined
siblings must also exist.
g) In a skin class collection, a class name may only occur
once.
h) On a given tree level, a node identifier can only be used
once.
i) Only previously defined sub trees can be extended.
[...]
2. Spec Tools
a) Generate a report which contains root classes, skin
classes, and attribute classes (with a list of modules
which use those attribute and skin classes).
3. CSS Tests
a) Check if the selectors are a superset of the spec - or -
check if the spec is a superset of the selectors.
b) Check if the children are in the same order (clarity).
c) Check if the sub trees are in the same order
(inheritance).
d) Check if skin>attribute selectors match the spec.
4. CSS Tools
a) Generate a selector stub file.
b) Add missing selectors.
c) Remove selectors which don't exist in the spec.
5. HTML Tests
a) If a root node is found, check if the structure matches
the specs (the content of the leafs is arbitrary).
b) Find unspecified classes and ids.
c) Ensure classes are in the order: base-root, ext-root, skin
(clarity).
6. Content Management Systems
a) Populate drop-down lists with the relevant skin classes to
customize components/controls/widgets.
b) Offer skin classes in drop-down lists in rich text
editors (e.g. for different headings).
EXAMPLE SPEC FILE
-----------------------------------------------------------------
// quantifiers:
// * = 0-n
// + = 1-n
// ? = 0-1
// n = n (default = 1, i.e. if the quantifier was omitted)
// ~ = inherit the quantifier
// Quantifier on root nodes are illegal, because they are silly.
// It's always 0-n (or 0-1 if it's an id). That's the big idea.
// general layout
.page[oldSchool gs960 liquid]{
?.head
.body{
*.leftCol[gMail gCal yahoo myYahoo]
*.rightCol[gMail gCal yahoo myYahoo]
.main
}
?.foot
}
// standard module
.mod[login universe comment]{
.top{
.tl
.tr
}
.inner{
?.hd
.bd
?.ft
}
.bottom{
.bl
.br
}
}
// extends .mod and changes the quantifier for .hd
.dualhead(.mod){
.inner{
// unlike .mod, this one got 2 mandatory .hd elements
2.hd
}
}
// extends .mod and adds an "a" element
// the other siblings must be listed, otherwise the position
// would be ambiguous
.boxlink(.mod){
.inner{
~.hd
~.bd
~.ft
a
}
}
.foo(.mod){
// this should generate an error since the position cannot be
// determined
.bar
}
// complex extends mod
.complex(.mod)
// pop extends mod
.pop(.mod)
// media block
.media{
?.img
.bd
?.imgExt
}
// data table - this is pretty much the worst case scenario
.data{
+table[txtL txtR txtC txtT txtB txtM]{
?thead{
*tr[txtL txtR txtC txtT txtB txtM]{
*th[txtL txtR txtC txtT txtB txtM]
*td[txtL txtR txtC txtT txtB txtM]
}
}
?tfoot{
*tr[txtL txtR txtC txtT txtB txtM]{
*th[txtL txtR txtC txtT txtB txtM]
*td[txtL txtR txtC txtT txtB txtM]
}
}
?tbody{
*tr[txtL txtR txtC txtT txtB txtM]{
*th[txtL txtR txtC txtT txtB txtM]
*td[txtL txtR txtC txtT txtB txtM]
}
}
*tr[txtL txtR txtC txtT txtB txtM]{
*th[txtL txtR txtC txtT txtB txtM]
*td[txtL txtR txtC txtT txtB txtM]
}
}
}
// grids
.line{
+.unit[lastUnit size1of1 size1of2 size1of3 size2of3 size1of4
size3of4 size1of5 size2of5 size3of5 size4of5]
}