The Trickery it Takes to Create eBook-Like Text Columns

Avatar of Chris Coyier
Chris Coyier on

There’s some interesting CSS trickery in Jason Pamental’s latest Web Fonts & Typography News. Jason wanted to bring swipeable columns to his digital book experience on mobile. Which brings up an interesting question right away… how do you set full-width columns that add columns horizontally, as-needed ? Well that’s a good trick right there, and it’s a one-liner:

columns: 100vw auto;

But it gets more complicated and disappointing from there.

With just a smidge more formatting to the columns:

main {
  columns: 100vw auto;
  column-gap: 2rem;
  overflow-x: auto;
  height: calc(100vh - 2rem);
  font: 120%/1.4 Georgia;
}

We get this:

Which is so close to being perfect!

We probably wouldn’t apply this effect on desktop, but hey, that’s what media queries are for. On mobile we get…

That herky-jerky scrolling makes this a bad experience right there. We can smooth that out with -webkit-overflow-scrolling: touch;

The smoothness is maybe better, but the fact that the columns don’t snap into place makes it almost just as bad of a reading experience. That’s what scroll-snap is for, but alas:

Unfortunately it turns out you need a block-level element to which you can snap, and the artificially-created columns don’t count as such.

Oh noooooo. So close! But so far!

If we actually want scroll snapping, the content will need to be in block-level elements (like <div>). It’s easy enough to set up a horizontal row of <div> elements with flexbox like…

main {
  display: flex;
}
main > div {
  flex: 0 0 100vw;
}

But… how many divs do we need? Who knows! This is arbitrary content that might change. And even if we did know, how would we flow content naturally between the divs? That’s not a thing. That’s why it sucks that CSS regions never happened. So to make this nice swiping experience possible in CSS, we either need to:

  • Allow scroll snapping to work on columns
  • Have some kind of CSS regions that is capable of auto-generating repeating block level elements as-needed by content

Neither of which is possible right now.

Jason didn’t stop there! He used JavaScript to figure out something that stops well short of some heavy scrolljacking thing. First, he figures out how many “pages” wide the CSS columns technique produces. Then, he adds spacer-divs to the scrolling element, each one the width of the page, and those are the things the scrolling element can scroll-snap to. Very clever.

At the moment, you can experience it at the book site by flipping on an optional setting.