Skip to content

Multi-column manipulation

Publish date: Author: Heydon

Despite predating both Grid and Flexbox, Multi-column Layout represents—at least to me—an even more radical departure from the way we normally do and think about CSS layout. Dividing just one element into a multi-column representation of its contents feels weird, heretical even.

Setting a multi-column context means asking (flow) content to progress, by column, in a horizontal direction. This invokes one of two issues, depending on whether you set a height on the element.

With no set height, there's no limit to the height of the columns. This will result in vertical overflow, and the necessity to scroll down and up the page to read each successive column. Many are likely to find this arduous.

A zig zag line illustrates the reading direction of multiple columns

With a set height, columns are forced to spawn in the inline (horizontal) direction, creating horizontal overflow. Setting overflow: auto frames this correctly.

.columns {
height: 25vh;
/* ↓ columns defined by width */
columns: 30ch;
overflow: auto;
}

However, despite the increasing popularity of scrolling menus and other such patterns harnessing horizontal scrolling, it is still an unconventional interaction paradigm — and unconventional patterns are liable to be misapprehended by users. There are ways to increase perceived affordance, of course—perhaps by adding some custom styling to the scrollbar (-webkit-scrollbar), or providing some overflow-dependent shadows. But this may not be enough.

Quantity-dependent columns

One thing I have been experimenting with is the application of multiple columns in response to content quantity.

Let's say I have a bullet list, and let's assume each bullet point is likely to be relatively short; no more than a sentence. There's no benefit to dividing the list into columns when there are only a few points. But the overall height of the list is shortened (and the chance of the reader being able to see the whole list without scrolling is increased) when a long list is divided into two.

Left: a one column list breeches the viewport at the top and bottom. Right: the list is split into two and can now be contained by the viewport height

In the following example, the list is split into two columns where there are 5 or more list items.

ol, ul {
columns: 2;
column-gap: 1rem;
}

li {
column-span: all;
}

li:nth-last-child(n+5),
li:nth-last-child(n+5) ~ * {

column-span: none;
}

By default, column-span is set to all, meaning each list item ignores the two-column mandate. A quantity query (the final declaration block) then resets column-span to none where 5 or more list items are present. Despite the misleading none value, this means list items will span one of the two columns.

Three columns. The first element in just one column wide because it has column-span: none. The second element is the width of all three columns, because it has column-span: all.

To follow is a live demo. Try opening up developer tools and removing a couple of list items. Note that this behavior is not currently available in Firefox. There is a bug open to implement column-span. Thank you to Erik Wallace for finding it.

  • Ac Feugiat Ante Sed Mauris Iaculis Ac Feugiat Quam Id
  • Vulputate Non In Diam Ornare Feugiat Sapien Cubilia
  • Posuere Id Mi Morbi Ornare Quam Nullam Amet A
  • Tristique Felis Nibh Accumsan Ex Odio Non Tellus
  • Ante Sit Quam Quam Tempus Laoreet Sed Morbi Sollicitudin Varius
  • At Congue Tempus Ligula Iaculis Euismod Phasellus Neque Consequat Eu

Block direction overflow

As Rachel Andrew has proposed, it would be beneficial to be able to control both the inline and block overflow direction. The support of block overflow would mean we could assume control over both column width and height. So long as the chosen height is no taller than the current viewport, the repetitive vertical scrolling issue described above disappears.

Two rows, each of four columns of text. The first row is entirely within the height of the viewport

Image caption: Reading down columns no longer necessitates vertical scrolling.

How block overflow direction is implemented and exposed is still up for grabs so, if you have any ideas, you may want to voice them.

My initial thinking is that a column-height property should be supported alongside column-width and column-count. The columns shorthand property would then need to take height as a third parameter.

Currently, columns takes column-width and column-count in any order—presumably because parsing can identify which is a length value and which is just an integer. This becomes more complex with two length parameters, so an expected order for these properties may need to be set. If that expected order is width-before-height, then the following values would be considered valid (where 30ch represents the width, and 25vh represents the height):

.columns {
columns: 30ch 25vh;
}

.columns {
columns: 30ch 25vh 3;
}

.columns {
columns: 30ch 3 25vh;
}

.columns {
columns: 3 30ch 25vh;
}

It's worth noting that 'hard-coding' a column-count is not likely to be useful in most cases, since both columns and rows are now being dynamically provisioned based on the available space. It's also worth noting that column-width sets an ideal width, not a fixed one, much like flex-basis. This eliminates overlaps and gaps.

The column-gap property injects space between columns. With support of column-height in place, row-gap would have to be supported as well. Although you are probably more familiar with the CSS Grid-specific properties grid-gap, grid-row-gap, and grid-column-gap, Firefox already supports the generic gap, row-gap, and column-gap properties for Flexbox. The intention is to normalize gap across the Grid, Flexbox, and Multi-column modules.


If you find yourself wrestling with CSS layout, it’s likely you’re making decisions for browsers they should be making themselves. Through a series of simple, composable layouts, Every Layout will teach you how to better harness the built-in algorithms that power browsers and CSS.

Buy Every Layout