CSS template-based layouts, or something like them, have been a long time coming. John Resig has blogged about them recently, echoing the attitudes of a few people, it seems. I generally agree: this looks great, and will be a vast improvement for HTML+CSS web development: finally HTML document structure will be largely separate from visual layout. This is something that CSS grids/tables completely fail to do – divs still have to be in row>column order: a semantic change from HTML tables, and nothing more, and they still aren’t supported by ie yet anyway (EDIT: Xanthir points out below that I was confused: CSS3-grid is actually a completely separate proposal to tables, and it’s basically the same as what I suggest here, albeit without the ability to name the grid).

But…

Yep, of course there are a few things I’m concerned about (and as there should be – if there weren’t I’d know I hadn’t been looking hard enough). First, there are a few minor points (Disclaimer: I may have missed or misunderstood parts of the spec. Feel free to correct me):

Minor Issues

  • The value the template element has been called the return of ASCII-art. It’s not. The layout doesn’t have to visually line up – you can have multiple columns on one line. But if it is used on one line, how does the last row height (/Xem) work? Does it conflict with the column heights that follow? Could this be made more separate? Maybe a comma after each row, and another symbol after the last row? Actually, that wouldn’t be needed, since the column-spacing row doesn’t start with a quote mark. Something like:
    Example VI modified version
    body {
    height: 100%;
    display: "a   .  b  .   c"  /2em
             ".   .  .  .   ."  /1em
             "d   .  e  .   f"
             ".   .  .  .   ."  /1em
             "g   .  h  .   i"  /2em
             5em 1em * 1em 10em
    }
    body {
    height: 100%;
    display: "a   .  b  .   c"  2em,
             ".   .  .  .   ."  1em,
             "d   .  e  .   f",
             ".   .  .  .   ."  1em,
             "g   .  h  .   i"  2em,
             5em 1em * 1em 10em
    }

    This would be somewhat more understandable on a single line (same example, without the filler columns and rows):

    Example VI
    display: "a b c" /2em "d e f" "g h i" /2em 5em * 10em;
    Modified
    display: "a b c" 2em, "d e f", "g h i" 2em, 5em * 10em;
  • This solution still has the problem that if 5 columns are defined in the diagram, and only 3, or 6 column widths are defined, something weird will happen. I guess it wouldn’t be hard with 3 to just add define the first three columns, and us “*” for the 4th and 5th. And for 6, just drop the last one… (See “Backwards Compatibility and Graceful Degradation, below)
  • What happens with odd shapes? Automatic fail? Like this:
    "aaaccd" < a-defined cells odd-shaped
    "aabbbd"
    "aabbbd"
    "aadddd" < L-shaped d cell

Not Quite a Complete Solution

This implementation, while far better than what’s been the standard so far, is still somewhat limited, in two major ways:

  • Sub-Child problem: The layout is restricted to direct child elements, meaning that two elements of html that should appear within one another structurally can’t appear side by side visually. Also, sub-children can’t become part of the parent layout (I assume). This is not a killer, since most likely there aren’t going to be many situations in which this will need to happen: usually visual and navigational elements should be fairly separated from the main content element, and all three within the same parent element. (EDIT: this isn’t actually a Sub-Child problem, it’s a cell-naming problem, see comments below)
  • The method of defining the layout could be problematic if it needs to change on-demand. Changes might occur either because some parts don’t appear on some dynamically generated pages, or because things move as JavaScript changes DOM elements. I think this has the potential to be painful: basically the entire layout defined by the parent element would have to change depending on the page, so every possible page layout combination would need to be defined (and each combination would need it’s own CSS identifier). I can’t see a way to easily dynamically change the Ex.VI layout (above). I guess this could be overcome by having nested templates, but that would make the sub-child problem worse.
  • This is mostly a pet hate, and probably isn’t really relevant, but the display: property is already used in css. I disagree with the display: table-* values already. It means you can’t define an element as a table element AND as “hidden” or “none“, but I guess they are usually mutually exclusive anyway. Nonetheless, something like layout: or template: would make more sense to me (I think the table-* values should also have their own property, perhaps grid:…)

A better way?

Might there be a better way? Nicholas Shanks has a decent idea in a comment on John Reisg’s blog post. The basic idea has a number of benefits:

  • It appears to fit more with the general CSS style of defining properties and values.
  • It would be much easier to dynamically change, either server-side or client-side.
  • It has the potential to completely remove the sub-child-as-cell problem (See below).
  • It would be harder to fuck up (impossible to make non-rectangular shapes).
  • It would allow overlaps and intrusion (See the graphic)!
  • It works with the already existing width: and height: properties (although maybe the current proposal does too).

Nicholas’s version is not without it’s own problems though. I don’t understand why he uses CSS3 pseudo-elements. I think a grid element should definitely be a whole element, just as a <td> cell is in an HTML table. His method also involves no way of naming the template (and thus escaping the sub-child problem, see below). His method may take up more text in a CSS file, depending on the layout.

My proposal is a modification of Nicholas’s, which addresses the first two of these problems, and maybe the third (haven’t thought about a lot of cases yet).

The parent element would be defined thus:

#container {
template: "container name"
}

and the child thus:

.cell {
template-cell: "container name";
template-cell-col: 1;
template-cell-row: 1;
template-cell-rowspan: 2;
template-cell-colspan: 1;
}

Where template-cell: could contain -row and -col, like the background: property can contain background-image: and background-color:. I don’t know how -rowspan and -colspan: would fit into that, but perhaps -row: and -col: could accept row/colspans like:

template-cell-col: 1-3;

(for spanning columns 1 to 3, I’m not sure if the hyphen is an appropriate symbol).

Ultimately, it could be condensed thus:

.cell {
template-cell: "name" 1-2 2;
}

The naming is important, because it means that the cell elements don’t rely on their place in the document structure to find out what their parent element is. The child can be inside 50 nested divs, but once parsed, it will be ripped out, and become a higher-level element (though only visually). This could also be done by simply referencing an ID. An ID could reduce the possibility reduce the chance of accidentally defining the  template: property in a generic element, like <div>, but it would loose some of similarity in style between the parent and cell property definition.

Comparing a complex example:

Example XVII Proposed version
body {
display:
"A  A  A  A  A  A  A  A  A" /5cm
".  .  .  .  .  .  .  .  ." /0.25cm
"B  .  C  C  C  C  C  C  C"
"B  .  C  C  C  C  C  C  C"
"B  .  C  C  C  C  C  C  C"
"B  .  C  C  C  C  C  C  C"
"B  .  C  C  C  C  C  C  C"
"B  .  D  D  D  D  D  D  D"
"B  .  D  D  D  D  D  D  D"
"B  .  E  E  E  .  F  F  F"
"B  .  E  E  E  .  F  F  F"
"B  .  E  E  E  .  F  F  F"
 * 3em * 3em * 3em * 3em *
}

h1 {
position: a;
margin-bottom: 1.5em
}

#toc {
position: b;
margin-right: -1.5em;
}

#leader {
position: c;
}

#art1 {
position: d;
}

#art2 {
position: e;
}

#art3 {
position: f;
}
body {
template: "main";
height:5cm;
}

/* dealing with gap rows/cols */
#headerspacer {
template-cell: "main" 1 1-9;
height: 0.25cm;
}

#leftspacer {
template-cell: "main" 1 1-9;
width: 3em;
}

#bottomspacer {
template-cell: "main" 10-12 6;
width: 3em;
}

h1 {
template-cell: "main" 1 1-9;
margin-bottom: 1.5em
}

#toc {
template-cell: "main" 3-12 1;
margin-right: -1.5em;
}

#leader {
template-cell: "main" 3-7 3-9;
}

#art1 {
template-cell: "main" 8-9 3-9;
}

#art2 {
template-cell: "main" 10-12 3-5;
}

#art3 {
template-cell: "main" 10-12 7-9;
}

This solution has a more CSS feel – the Working Draft proposal has already been described as looking “a bit hacky” by Ben Ward (he is still very approving of the proposal though). The largest problem with this is that it doesn’t deal with the 3ems on the 4th, 6th and 8th column. If these were required, more spacer elements could be added (although I already see the current ones as redundant – why not use margins?). I don’t quite understand their purpose, but I assume it’s basically the same as min-width: 3em – that would have a similar effect, and reduce the complexity of the grid somewhat.

Obviously, this method probably has holes in it that I don’t see, if you see any, please point them out.

Backwards compatibility and Graceful Degradation

This is always going to be the biggest problem and can’t be overcome by the above solutions, and will require some kind of major-browser manufacturer collusion, if it’s to be accepted widely. I posted this RFE for gecko (firefox, etc) specifically for this problem: https://bugzilla.mozilla.org/show_bug.cgi?id=466974

Graceful degradation is the flip side of the same coin: document structure will have to maintain some visual usefulness if the template fails. Templates will fail, either because they aren’t implemented in the browser, or because they have a bad value – mis-matched column numbers, non-rectangular layouts (L shaped boxes, etc). But ultimately, the spec is implemented accros browsers, that will be the designer’s problem, and not the fault of the browser implementation. Once this (or something like it) is implemented accross browsers, structure will finally be free from layout constraints.

Conclusion

I offer my solution in the hopes of making people think about things in ways they haven’t before, and of kindling discussion. If the final outcome is basically what the Advanced Layout Module Working Draft already proposes, then so be it. Nothing is ever perfect, there are no backwards steps in any of the solutions already put forward. What ever happens it will be good. Let’s hope it can happen soon.

About these ads