Posts from Friday, January 15th, 2016

Subgrids Considered Essential

Published 8 years, 2 months past

Grid layout is a pretty darned fantastic thing.  I’ve been digging into it as I write the grid layout chapter of CSS:TDG4e, and there are more things in here than have been dreamt of in our layout philosophies.  One of the things I’ve recently come to realize is the power and necessity of subgrids.

To understand why, let’s explore a use case, which you can see in various forms on this test page.  I cribbed this use case pretty directly from Fantasai, but I’m going to be analyzing it a little differently.  The basis is a form with the various fields and labels in unordered list items, to help with accessibility and to be basically readable and usable if the CSS somehow fails to be applied.  The markup looks like this:

<form method="post" action="script.cgi">
  <ul>
    <li><label for=name>Name:</label>
        <input id=name name=name></li>
    <li><label for=email>Email:</label>
        <input id=email name=email type=email></li>
    <li><label for=tel>Telephone:</label> 
        <input id=tel name=tel type=tel></li>
    <li><label for=url>Homepage:</label>
        <input id=url name=url type=url></li>
    <li><label for=bday>Birthday:</label>
        <input id=bday name=bday type=date></li>
  </ul>
</form>

The basic form with no grids.

Ideally, we’d like the form fields to all line up into a column, and the labels to be in another column.  Furthermore, we’d like the label column to be only as wide as the widest label’s element box, and no more; the rest of the grid can be taken up by the input column.

This seems like a case tailor-made for grid layout, but there’s a problem.  Because grid items are only ever the child elements of a grid container, we can’t just apply a grid to the ul element and go from there.  Instead, we have to make each li element a grid container, in order to make each label and input element a grid item.  But that means each list item’s grid affects its children, with no reference to the other list items’ grids.  We can write the template we want, like so:

form ul li {display: grid;
  grid-template-columns: [start label] max-content [input] 1fr [end];}

Each list item a grid, but to what end?

That will get us a result almost precisely as if we’d applied no grids at all.  The only difference is that the input elements will all be as wide as their columns, according to the CSS Grid specification.  Chrome fails to do this last bit correctly, whereas Firefox Nightly gets it right, but otherwise the layout is essentially the same.  You can see this in the #form1 example on the test page.  (Remember, you have to have a current grid-supporting browser for the examples to correspond to what I’m talking about here.)

We can get closer to our goal by experimenting with a fixed-width grid column for the labels, figuring out the width of the widest label, and then just making all the label columns be that wide.  That would be written something like this:

form ul li {display: grid;
  grid-template-columns: [start label] 7em [input] 1fr [end];}

Using fixed-width columns to simulate a single column.

That works pretty well so long as none of the labels ever change — if a label is added (or edited) to be wider, it will wrap to multiple lines.  Alternatively, if the longest label is dropped or edited to be shorter, the label column won’t resize down.  It will just stay the same dumb fixed width until someone hand-adjusts it.  And honestly, at that point I may as well be using flexbox, which would do this version of the layout just as well, and would be more widely supported for the near to intermediate future.  At any rate, you can see the grid version of this in the #form2 example on the test page.

But what if we could set a grid on the ul element, and then make all the li elements grids that use their parents’ grid for the layout of their children?  That’s exactly what subgrids do.  This is the solution we’ve been seeking, in basic form:

form ul {display: grid;
  grid-template-columns: [start label] max-content [input] 1fr [end];}
form ul li {display: grid; grid: subgrid; grid-column: start / end;}

Here the list items establish grid containers, thus making the label and input elements into grid items like before, but are stretched across the two columns of the ul while using those very grid lines for laying out their child elements, plus those children influence the placement of their grandparent’s grid lines.  Thus, we can specify things like max-content for the label column size and have it Just Work™.

Or it would Just Work™, except that as I write this, none of the grid implementations have subgrid support.  Authors who want to create the kind of layout we’re after have to compromise in one way or another — either by faking the content-sizing with a fixed-width column, or by stripping down the markup until there’s barely anything left — thus sacrificing accessibility, progressive enhancement, and general best practices, as Fantasai illustrated in her article.

You can probably see a lot of other ways in which subgrids would be useful.  Take defining a full-page grid, the kind with a bunch of regularly repeating grid lines onto which various elements can be attached, like this one or this one.  In this scenario, being able to designate each section of the page a subgrid would let you have all the pieces inside that section participate in, and lay out in relation to, the overall page grid.  Without subgrids, you’d either have to make every element you want to lay out a child of the body element (or whatever you used to create the page grid), or you’d have to recreate segments of the page grid in each nested grid, and give up any hope of columns that can flex with the contents of multiple page sections.  Neither solution is appealing.

This is why I’ve come to the same conclusion other grid experts (like Rachel) already have: subgrids are a major component of grid layout, and should be a part of any grid layout implementation when it emerges from developer-preview status.  If that means delaying the emergence of grids, I think it’s worth it.

I say that because our field has a tendency to glom onto the first iteration of a technology, learn it inside out, hack around its limitations, and then ignore any future improvements unless somehow forced to do so.  If grid layout is released without subgrid support, we’re risking shoving subgrids into the back of the author-practices cupboard for a long time to come.  And along with it, potentially, grids themselves.  The frustration of trying to build layouts without subgrids will quickly become overwhelming for all but the simplest cases, leading authors to try and then discard grids as a serious tool.

First impressions matter.  CSS itself suffered for years from the initial impressions designers formed of “boring, boxy” layouts, and it still suffers from the handicap of being a presentation system without a layout engine at its core.  Grid layout is the first serious candidate to fill that hole in the past two decades, and I don’t want to see them hamstrung from the outset.  Subgrids are essential to the adoption of grids.  I hope they’ll be implemented as soon as possible, and before grids are pushed into public release channels.


Browse the Archive