Line-tracking using plain CSS 🧨

Line-tracking using plain CSS 🧨

  • bookiza
  • 3 minutes
  • January 8, 2020

One of the best and yet lesser-known features of a physical book is line-tracking.

If you haven’t heard about line-tracking before worry not, we have your back covered:

Line-tracking

Line-tracking is a means of organizing long blobs of texts along “tracks” on a spread in such a way that our eyes begin to groove along a path of continuity and punctuation in thought.

Whoa. What’s that?

And did you notice the word spread thrown in there somewhere?

A spread is two pages side-by-side when you open a physical book in the middle, like so:


Format of a chapter book.
Oppulent and luxurious paragraphs with line-tracking.


Easy-peasy.

And the following illustration shows how line-tracking works and how the eyes work through a groove of thought with the text placed along a track.


Saccadic reading. Bubblin Superbooks.
Line-tracking on a flush body of text.


Traditional publishers often use this technique to format content along paginated codices to afford a better and stylish reading experience. Put simply, line-tracking refers to making the body of text suitable for the human eye to track along vertically balanced lines.

Good line-tracking often involves solving common issues like removing rags and rivers, typesetting and leading for flush and luxurious paragraphs. It involves eliminating widows and orphans and so on for packing and continuity-ala, format.

It’s all set in the stone of traditions that a dead-tree publisher likes to follow.

But do we need line-tracking on the web?

Yes, we do!

Since Bubblin is a traditional publisher of books on the web, we apply line-tracking on all our latest books. And our readers love it! Our readership has shot up by 400% and the number of people leaving a book midway has dropped by 17%.

But our story is about books, so offering line-tracking makes sense.

Do we need it on websites?

I suspect yes.

Although ordinary web design doesn’t look at line-tracking at all, at least not consciously, I think there is very a strong case for it. Let me explain:

Here is a quote about the web from 1997:



As you might have already noticed not much has changed about how content is consumed over the web today. Our online reading habits are still as haphazard as they used to be and we are both uninvested in and inundated by content on the information highway. This is by design.

With a loosely formatted scroll it is safe to assume that what we gain in speed, we lose in the attention span. The ability to concentrate and think critically along long blobs of text (or thoughts) is an important attribute to go after.

I think it was a good first step for the web to start with capricious surfing in mind but now we can focus on improving the ability to focus and remember among individuals.

And line-tracking is a perfect place to start on that.


Saccadic reading. Bubblin Superbooks.
The restless saccadic motion of the human eye while reading a partially formatted block of text.


Implementing line-tracking with CSS

The good news is that line-tracking is super easy to implement using plain CSS. Shown below is an implementation of a strong layout for a textbook type of Superbook which uses simple viewport units to specify typography and line-height.

The same can be implemented on responsive webpages just using vmin units instead for a website.

I think it is cool and is also well supported across vendors, but more importantly, the technique itself is super exciting. 🤩

The design aspect of a website or a Superbook is left out of the discussion below on purpose.

Meet Gadda-Gadda CSS

The layout template shown below uses vw unit instead of vmin because a page on a codex Superbook is always in portrait orientation. You’ll have to swap vw with vmin if you wish to apply the technique on a website (See modern baseline for more clarity).

Notice the helper classes p.squeeze-para and p.stretch-para that pin orphans and widows near page breaks.

html,
body {
  background: #fff;
  margin: 0 0;
  overflow: hidden;
  color: #000;
  height: 100vh;
  width: 100vw;
  font-family: 'EB Garamond', serif;
  font-style: normal;
  font-synthesis: none;
  font-stretch: ultra-condensed;
  font-variant: no-common-ligatures proportional-nums slashed-zero;
  font-size: 4vw;
  line-height: calc(4vw * 1.50);
  font-kerning: normal;
  text-rendering: geometricPrecision;
}

h1,
h2,
h3,
h4,
h5 {
  width: 100%;
  text-align: center;
  line-height: calc(2 * 4vw * 1.50);
}

h5,
h6 {
  line-height: calc(1 * 4vw * 1.50);
}

h1,
h2,
h3,
h4,
h5,
h6 {
  font-family: 'Berkshire Swash', cursive;
  margin: 0 0;
  padding: 0;
  color: #000;
  font-weight: 100;
}

h1 {
  font-size: 8vw;
}

h2 {
  font-size: 6vw;
}

h3 {
  font-size: 5vw;
}

h4 {
  font-size: 4vw;
}

h5 {
  font-size: 4vw;
}

h6 {
  font-size: 4vw;
}

img {
  border: 0;
  -ms-interpolation-mode: bicubic;
  display:block;
  margin: calc(1/2 * 4vw * 1.50) auto;

}

a {
  color: #0077ff;
  outline: 0 none;
  text-decoration: none;
}

a:focus,
a:active,
a:hover {
  outline: 0 none;
  color: #070;
}

a:active {
  text-shadow: 0 0 2px #fff;
}

p {
  text-indent: 5vw;
  margin: 0 0 0 0;
}

p.start-chapter {
  text-indent: 0;
  margin: calc(5 * 4vw * 1.50 - 2px) auto 0;
}

p.start-chapter:first-letter {
  display: inline-block;
  margin: 0 1vw 0 0;
  float: left;
  font-size: calc(3 * 4vw); /* three times normal size */
  line-height: calc(2 * 4vw * 1.50 * 0.95);
  font-family: 'Berkshire Swash', cursive;
}


blockquote {
  text-align: center;
  font-style: italic;
  position: relative;
  quotes: "\201C""\201D""\2018""\2019";
  margin: calc( 1/2 * (150vh *(1115/1443))/100 * 7/5) auto;
  margin: calc( 1/2 * 4vw * 1.50) 0;
  margin: 0 0;
  display: inline-block;
  width: 100%;
}

blockquote p, blockquote em {
  margin: 0 auto !important;
}

pre,
code {
  white-space: pre-wrap;
  white-space: -moz-pre-wrap;
  white-space: -pre-wrap;
  white-space: -o-pre-wrap;
  word-wrap: break-word;
  margin: 0;
  padding: 0;
  text-align: center;
  font-family: 'EB Garamond', serif;
  font-style: normal;
  font-synthesis: none;
  font-stretch: ultra-condensed;
  font-variant: no-common-ligatures proportional-nums slashed-zero;
  font-size: 4vw;
  line-height: calc(4vw * 1.50);
  font-kerning: normal;
  text-rendering: geometricPrecision;
  /*background: pink;*/
}

ol {
  margin: calc((4vw * 1.50)/2) 5vw;
  padding: 0;
  list-style-type: none;
  counter-reset: step-counter calc(var(--start) - 1);
}

ol li {
  counter-increment: step-counter;
}

ol li::before {
  content: counter(step-counter) ". ";
  margin-right: 1vw;
  color: black;
  font-weight: bold;
  padding: 0.3vw 0.8vw 0.3vw 0;
}

ol li.split-li {}

ol li.split-li::before {
  content: '';
  margin-right: -0.8vw;
  background-color: rgba(255, 255, 255, 1);
  color: black;
  font-weight: bold;
  padding: 0.3vw 0.8vw 0.3vw 0;
}

ul {
  margin: 0 0 0 5vw;
  padding: 0;
  list-style: none;
}

ul li {
  margin: 0;
  padding: 0;
}

hr.section {
  border: 0;
  text-align: center;
  height: calc(4vw * 1.50);
  /* Take a break of two lines only. */
  margin: calc((4vw * 1.50)/2) 0;
}

hr.section::after {
  content: "";
}

hr.section::before {
  content: "❦";
  color: black;
}

hr.chapter {
  border: 0;
  text-align: center;
  height: calc(4vw * 1.50);
  /* Take a break of two lines only. */
  margin: calc((4vw * 1.50)/2) 0;
}

hr.chapter::after {
  content: " ";
}

hr.chapter::before {
  content: "❦";
  color: black;
}

hr {
  border: 0;
  text-align: center;
  height: calc(4vw * 1.50);
  /* Take a break of two lines only. */
  margin: calc((4vw * 1.50)/2) 0;
}

hr::after {
  content: "";
}

hr::before {
  content: "❦";
  color: black;
}

/* Clearfix*/
.group:before, .group:after {
  content: "";
  display: table;
}

.group:after {
  clear: both;
}

.group {
  zoom: 1; /* For IE 6/7 (trigger hasLayout) */
}


/* Page specific wrapper classes. */

.leaf {
  height: 100%;
  height: 100vh;
  width: 100%;
  width: 100vw;
  margin: auto;
}

.inner {
  margin: calc((100vh - (17 * (4vw * 1.5) + 4vw))/2) auto;
  width: 85vw;
  height: calc(17 * (4vw * 1.5) + 4vw);
  max-height: calc(17 * (4vw * 1.5) + 4vw);
  break-after: right;
  display:inline-block;
  /* background: aliceblue; */  /* enable to see the maximum staging area */
}

.flex {
  display: -webkit-box;
  display: flex !important;
  align-items: center;
  justify-content: center;
}

.justify {
  text-align: justify;
}


/* Helper classes */

p.no-indent {
  text-indent: 0;
  /* Continue line on a new page with break on the previous page. */
}

p.stretch-last-line {
  text-align-last: justify;
  /* Not supported on Safari */
}

p.squeeze-para {
  letter-spacing: -0.5px;
  /* Handle orphans and widows manually */
}

p.stretch-para {
  letter-spacing: 0.5px;
  /* Handle orphans and widows manually */
}

.uppercase {
  text-transform: uppercase;
}

.pad-vertically {
  padding: calc(4vw * 1) 0;
}

.center {
  text-align: center;
  margin: inherit auto;
}

.small {
  font-size: 80%;
}

H2S CLI

We use a simple nodejs based cli h2s (a paginator library) to split “markdown compatible” HTML into a stack of webpages for a Superbook, like so:

Place your markdown-compatible sanitized.html inside the ./interim folder (sibling to the manuscript directory) of your Bookiza app.

Then, execute the following steps in the order of description:


1. $ h2s objectify // Turns HTML (manuscript) into a set of key: value pairs.

2. $ h2s pagify    // Paginates the key: value pairs according to a responsive layout at ./templates folder.

3. $ h2s bookify   // Apply layout on the paginated HTML and build its final render.

Start the $ bookiza server to load the book on your browser (localhost:4567) and review the book manually. Linked below is a live book that implements intrinsic line-tracking as discussed above:


Chrome & Firefox support only.


Your opinions on this essay and the demo are very welcome.

About the author

Sonica Arora

A tree hugger at heart. Code, stories & coffee.

https://bubblin.io/sonica