Tab Completion

I'm Tab Atkins Jr, and I wear many hats. I work for Google on the Chrome browser as a Web Standards Hacker. I'm also a member of the CSS Working Group, and am either a member or contributor to several other working groups in the W3C. You can contact me here.
Listing of All Posts

Stacked Layout Proposal

Last updated:

[As usual, this is a personal draft/proposal, and carries no endorsement or support from the CSSWG.]

The CSS2.1 layout modes were primarily designed for laying out documents, not webapps. Webdevs have gone to heroic efforts to bend the 2.1 layout modes to actually work for webapps and have done some amazing things, but it shouldn't be that hard. Two relatively new specs coming out of the CSSWG for CSS3 are addressing this problem - Flexbox and Grid Layout - but there are a few more places where we could solve a problem with a simple, targetted layout solution.

One in particular is stacked layout, where multiple elements are stacked atop each other, and only the topmost is shown at any one time. This is used quite commonly in "tabbed" displays, for example, like your browser window.

Can This Be Done via Existing Approaches?

A stack can be thought of as similar to a flexbox that happens to be aligned on the Z axis, rather than the X or Y. However, attempting to graft this into flexbox doesn't actually work well. You need a lot of new controls to make it work properly, such that you haven't actually gained anything. It's just not worth the complexity.

Attempting to graft this into Grid is a bit easier. When you set display:grid, the initial value of grid-rows and grid-columns set up a single cell that spans the element, and the initial value of grid-row and grid-column places all the grid items in the cell at (1 1). So, just be setting the display, you get all the children stacked together, with grid-layer controlling which element is on top. This still has a few problems, though. The elements are literally stacked on top of each other, so if they're different sizes or partially transparent you'll see the lower elements. Most of the time you actually want the "lower" elements completely hidden.

display:stack

So, this needs to be done as a separate layout mode. It seems like we can get away with something really simple, luckily.

Start with a simply display:stack addition. It would borrow a lot of language from Flexbox or Grid in terms of defining children as being "stack items", handling block formatting contexts, etc.

Add a property for controlling the order. We can either borrow from Flexbox and get stack-order or borrow from Grid and get stack-layer. Alternately, since we're only showing a single item from the stack, we could set up a simple property that just accepted, say, top and auto - it shows the first element in source order with top set. Another possible direction is a property set on the stack itself giving an index into its stack items, denoting which one is on top. That has the advantage of only declaring a single "top" element. This needs some thought.

Add a property for controlling the size of the stack. By default, the stack could size itself to the size of its widest and tallest children, but the property could instead make it size itself according to the current "top" child.

Finally, add properties for aligning children within the stack, when they're smaller than the stack element itself. Grid's already got a pair of alignment properties that do what we want; we'd just have to change the names.

Attaching Tabs to the Stack

The only bit that gets complicated is that most stacks expose a set of tabs connected to the stack items, so that selecting a tab promotes the associated stack item to the top. This sort of connection is hard to do in CSS, so I'm not sure how we'd do it. It's possible that this is kept in JS, and we just handle the layout of the stack itself in CSS.

If we did do this in CSS, though, here's some initial ideas:

Another display type, like display:stack-tab, for declaring an element to be a tab. If the tab is a direct child of a stack, it's automatically associated with the following stack item. Otherwise, if it's inside of a stack item, it's associated with that stack item. Otherwise, it's equivalent to display:block.

The stack itself would then grow two child boxes - one for the tabs, and one for the stack items. The tabs would automatically be moved to the tab container (possible through some automatic Regions machinery) where they can be styled normally. The position of the tab container would be controlled by another property. The container is accessible through a pseudo-element and can be styled normally as well. Default it to display:flexbox with a good direction, so the tabs can reorder themselves with a simply flex-order declaration.

The only remaining magic is setting one of the stack items to be the top based on activating tabs. This lends some weight to the "property on the stack" approach for choosing the top stack item - rather than a numeric index, you can just set "active-tab" as the value, and whenever a tab is "activated" (whatever that means), it changes the top stack item. The tabs themselves would also need some way to detect when they're "active", probably via a pseudoclass like :active-tab.

Doing this more than doubles the complexity of the spec, unfortunately, and it still might not be ideal for all cases. Spec design is hard!

(a limited set of Markdown is supported)