Continuous Typography

Notes to­ward a con­tin­u­ous frame­work for screen ty­pog­ra­phy

Here are some notes about an idea I’ve been call­ing Continuous Typography for the sake of think­ing about it. It’s a way of think­ing about ty­pog­ra­phy in terms of con­tin­u­ous func­tions, rather than ab­solute val­ues.

Functions (you’ll re­call from your maths text­book) pro­duce dif­fer­ent re­sults based on one or more in­put pa­ra­me­ters. For ex­am­ple, the func­tion $f(x) = 3x + 2$ will re­turn dif­fer­ent re­sults de­pend­ing on the value of its in­put pa­ra­me­ter $x$.

If we ap­ply this idea to ty­pog­ra­phy, it al­lows us to make de­sign de­ci­sions rel­a­tive to vari­able in­put pa­ra­me­ters like screen size, con­nec­tion speed, user pref­er­ences, and so on. This can ap­ply to any en­vi­ron­ment, but it’s es­pe­cially use­ful for type­set­ting on the web.

Note that most of these ideas aren’t very orig­i­nal any­more, I’m largely syn­the­sis­ing here for my own un­der­stand­ing. Take a look at the foot­notes for the orig­i­nal sources.

The Problem

When you’re de­vel­op­ing a piece of ty­pog­ra­phy you have to de­fine a se­ries of re­la­tion­ships:

There are all kinds of meth­ods to do this (Bringhurst fills a whole chap­ter with them in Elements of Typographic Style1), but in any case you even­tu­ally ar­rive at a set of val­ues for your mea­sure, type size, weight, spac­ing and so on that pro­duce what­ever vi­sual ex­pres­sion you set out to achieve.

A paragraph is set in a serif typeface. Values for measure, font size, line height, etc. are shown in red.
A block of text type­set with ab­solute val­ues.

Sample text from Flexible Typesetting by Tim Brown.

Take the font size, for in­stance: We want to set this so it gives the right voice to the piece of writ­ing we’re work­ing with, but it also has to be ap­pro­pri­ate to the type­face we’ve cho­sen, the size of the page, and it should re­sult in a com­fort­able num­ber of char­ac­ters per line. Other ad­just­ments fol­low from it: A change in type size might com­pel dif­fer­ent spac­ing, a change in weight, hy­phen­ation, and so on.

In print, you tweak these val­ues un­til you ar­rive at a set of num­bers that pro­duces the vi­sual ex­pres­sion you aimed for. And be­cause you’re work­ing with a piece of pa­per of fixed di­men­sions and per­ma­nent ink, you can be fairly sure that the num­bers you’ve es­tab­lished will stay in­tact through­out the pro­duc­tion process, and land in the read­er’s hand just how you in­tended.

But on the web, this method starts to fail. Unlike a pa­per sheet, the browser win­dow your text will be viewed in is com­pletely vari­able; it can take on any size and ap­sect ra­tio what­so­ever. If we set our font size to a fixed num­ber (18px, say), the re­la­tion­ship be­tween it and the browser win­dow will be dif­fer­ent on every screen, and un­pleas­ant on most.

And the size is of the browser win­dow is­n’t the only vari­able in play: Readers can mod­ify type size and colours through their browser or op­er­tat­ing sys­tem, or have your text trans­lated into their own lan­guage on the fly. Your choice of type­face might well be over­writ­ten by a user’s pref­er­ence or a failed net­work re­quest, and even the text it­self might change over time.

The tra­di­tional guide­lines of ty­pog­ra­phy about line-lengths, spac­ing, har­monies, and so on still ap­ply on the web; it’s just that we’re now try­ing to ap­ply them in a con­text where many of their pa­ra­me­ters have be­come vari­able. There are ways to lock down some of these pa­ra­me­ters - there’s a HTML snip­pet that pre­vents peo­ple from re­siz­ing your type, for in­stance - but that seems to me to run counter to the promise of the medium: that it works for any­one, any­where.

What we need is a way to make ty­po­graphic de­ci­sions in a way that is rel­a­tive to all of these vari­able pa­ra­me­ters, but still gives us some con­trol over the re­sult­ing vi­sual ex­pres­sion. The con­struct that lets us do this - gen­er­ate dif­fer­ent out­puts de­pend­ing on a set of in­puts with ar­bi­trary gran­u­lar­ity - is called a con­tin­u­ous func­tion.

A con­tin­u­ous ap­proach

Let’s think through this by defin­ing a sin­gle prop­erty of our text block  — the font size — as a con­tin­u­ous func­tion. Following the tra­di­tional ap­proach, we might de­fine the font size us­ing a CSS de­c­la­ra­tion like this one:

p {
font-size: 16px;
}

The 16px here is an ab­solute value. It’s go­ing to stay the same re­gard­less of the size of the screen, the read­er’s pref­er­ences, and any other out­side pa­ra­me­ter. As a re­sult, it might work fine on a tablet but will prob­a­bly feel a lit­tle lost on a big desk­top mon­i­tor, and un­com­fort­ably large on a phone.

But CSS gives us the tools to de­fine the font size in a way that does re­spond to out­side pa­ra­me­ters. For ex­am­ple, we could use the vw unit in­stead of pix­els to de­fine our font size:

p {
font-size: 1vw;
}

One vw is equal to one per­cent of the width of the read­er’s screen. So in the CSS de­c­la­ra­tion above we’re say­ing: The font size on para­graphs is equal to the width of the read­er’s screen mul­ti­plied by 0.01. That’s a con­tin­u­ous func­tion.

It pro­duces a dif­fer­ent font size for every screen size it en­coun­ters: On a screen that’s 1000 pix­els wide we get a font size of 10 pix­els, a 1500 pixel-wide screen re­sults in a font size of 15 pix­els, and so on. Drawn onto a co­or­di­nate sys­tem, it looks like this:

A linear function is drawn on a coordinate system. X: Screen width, Y: Font size
If we de­fine the font size de­fined as a con­ti­nous func­tion of the screen width, it forms a line.

I think this sim­ple draw­ing rep­re­sents a big shift in our ap­proach to ty­pog­ra­phy on the web. We’re no longer plac­ing a sin­gle point on the co­or­di­nate sys­tem (by defin­ing a sin­gle, ab­solute value), but a line con­tain­ing an in­fi­nite num­ber of points - our ty­po­graphic in­tent has be­come di­men­sional.

This idea does­n’t just ap­ply to font size, but every other as­pect of our text block: Measure, let­ter-, line- and word spac­ing, in­den­ta­tions, weight, vari­able font pa­ra­me­ters can all be de­fined as con­tin­u­ous func­tions of one or more in­put pa­ra­me­ters. The ty­pog­ra­pher’s work be­comes the shap­ing of these func­tions: How steep are they? Do they have min­i­mum and max­i­mum val­ues? Where are their in­flec­tion points? Are they smooth, jagged, sym­met­ri­cal, cycli­cal, ran­domised? How do they re­late to each other? By an­swer­ing these ques­tions one way or an­other, any de­sired vi­sual ex­pres­sion can be achieved for every reader.

In the fol­low­ing sec­tion we’ll look at ways this is al­ready pos­si­ble in CSS to­day, and what might yet be to come.

Shaping the func­tion

Slope

4 linear functions of different slopes are drawn on a coordinate system.
Different nu­mer­i­cal fac­tors pro­duce steeper and shal­lower curves.

A ba­sic way to ma­nip­u­late our func­tion is to de­fine its slope. We do this by mul­ti­ply­ing our in­put vari­able (1vw in our ex­am­ple) with a dif­fer­ent nu­mer­i­cal fac­tor:

p {
font-size: 0.5vw; /* This produces a shallow curve */
font-size: 1vw;
font-size: 2vw;
font-size: 5vw; /* This produces a steep curve */
}

Bigger nu­mer­i­cal fac­tors pro­duce steeper curves. A steeper curve, in this ex­am­ple, causes the font size to change more ag­gres­sively with the screen width.

Minimum and max­i­mum val­ues

3 linear functions with different minimum and maximum values are drawn on a coordinate system.
Minimum and max­i­mum val­ues pro­duce flat sec­tions on ei­ther side of the slope.

It’s of­ten use­ful to de­fine min­i­mum and max­i­mum val­ues for a given prop­erty. We can do this by us­ing the min() and max() func­tions in CSS:

p {
/* max() returns the larger of the two input values,
so this will never dip below 16px */

font-size: max(16px, 2vw);

/* min() returns the smaller of the two input values,
so this will never grow beyond 32px */

font-size: min(32px, 2vw);
}

We can also set both min­i­mum and max­i­mum val­ues at the same time us­ing the clamp() func­tion:

p {
/* This will produce a value between 14px and 32px */
font-size: clamp(14px, 1.5vw, 32px);
}

I tend to set these val­ues by eye, but be­cause we’re work­ing with func­tions we have the whole toolkit of math­e­mat­ics to draw on if nec­es­sary. For in­stance, we could use lin­ear al­ge­bra to cal­cu­late min­i­mum and max­i­mum val­ues that cor­re­spond to spe­cific screen sizes2, or lin­ear re­gres­sion to de­rive a curve from a given set of ab­solute val­ues. 3

Functions with mul­ti­ple pa­ra­me­ters

A plane is drawn on a 3d-coordinate system. Caption: Font size = Screen width × 0.01 + Reader's default font size × 0.85
If we de­fine the font size as a func­tion of the screen size and the read­er’s de­fault font size, it forms a plane.

So far, we’ve only looked at func­tions with a sin­gle in­put pa­ra­me­ter  — the screen width. But that’s not the only in­put we can use. For in­stance, it’s prob­a­bly a good idea to take into ac­count the de­fault font size the reader has set up in their de­vice set­tings, in ad­di­tion to the size of their screen. 4

We can use the calc() key­word to do this in CSS5:

p {
font-size: calc(1vw + 0.85rem);
}

Here we’re say­ing: The font size is equal to the width of the screen mul­ti­plied by 0.01, plus the read­er’s de­fault font size mul­ti­plied by 0.85. If we draw this func­tion onto a co­or­di­nate sys­tem, its out­put val­ues no longer form a line but a plane; our ty­po­graphic in­tent has gained a sec­ond di­men­sion.

There is no limit to the num­ber of in­put pa­ra­me­ters our func­tions can draw on. The read­er’s con­nec­tion speed, whether they have dark mode en­abled6, their read­ing dis­tance, their pre­ferred lan­guage, even the time of day at their lo­ca­tion may all be use­ful pa­ra­me­ters for multi-di­men­sional ty­po­graphic sys­tems.

The out­put of one func­tion can be­come the in­put pa­ra­me­ter of an­other, too. This is ex­actly what hap­pens when we set prop­er­ties like line-height to a unit­less value: it qui­etly pulls in the cur­rent font size as a pa­ra­me­ter.

p {
font-size: calc(1vw + 0.85rem);
line-height: 1.3; /* = font-size * 1.3 = (1vw + 0.85rem) * 1.3 */
}

Non-linear func­tions

A curved plane is drawn on a 3d coordinate system. Caption reads: Font size = f(x,y).

So far we’ve only looked at lin­ear func­tions, or func­tions that pro­duce straight lines when drawn on a co­or­di­nate sys­tem. But there is no con­cep­tual rea­son our ty­pog­ra­phy should be lim­ited to these. It’s en­tirely pos­si­ble we may need ex­po­nen­tial, si­nu­soid, stepped, ran­domised, or yet more ex­otic func­tion types to achieve spe­cific ty­po­graphic ex­pres­sions.

As I write this, there is no sim­ple way to do this in CSS. It is pos­si­ble to stitch to­gether mul­ti­ple lin­ear func­tions us­ing me­dia queries, and so ap­prox­i­mate more com­plex curves, but the code quickly be­comes un­wieldy. Sass in­cludes a pow­er­ful math mod­ule which can be used to ab­stract some of this com­plex­ity away, but a bar­rier to en­try re­mains. 7.

Mike Riethmuller (who de­vel­oped both of those so­lu­tions) sug­gests that a bet­ter way to achieve these non-lin­ear func­tions in CSS would be to make the Easing Module avail­able out­side of the an­i­ma­tion con­text, to which it is cur­rently bound8. This would be an el­e­gant so­lu­tion in­deed: the eas­ing mod­ule sup­ports many use­ful func­tion types (including Bezier curves, which ty­pog­ra­phers are al­ready fa­mil­iar with) in ad­di­tion to ba­sic lin­ear func­tions, and many de­sign tools al­ready in­clude pow­er­ful in­ter­faces to edit these curves vi­su­ally.

The rel­e­vant is­sue on the CSS Working group is still open, so we likely won’t see a browser im­ple­men­ta­tion of this soon.


A paragraph and small graph diagrams.
A block of text type­set with con­tin­u­ous func­tions.

Sample text from Flexible Typesetting by Tim Brown.

But re­gard­less of the pre­cise im­ple­men­ta­tion, I think that the idea that that any ty­po­graphic at­tribute (including vari­able font pa­ra­me­ters) can be a func­tion (linear, ex­po­nen­tial, stepped, Bezier, ran­dom, or oth­er­wise) of any given in­put vari­able (user pref­er­ence, screen di­men­sions, con­nec­tion speed, time of day, dis­play lan­guage, or what­ever else) is an in­cred­i­bly pow­er­ful one, and worth ex­plor­ing as an aes­thetic as well as a tech­ni­cal propo­si­tion. I’m al­ready us­ing ba­sic lin­ear func­tions in prac­tice with promis­ing re­sults.

I’m es­pe­cially in­ter­ested in what a vi­sual de­sign tool would look like if it was built on the model of con­tin­u­ous ty­pog­ra­phy. Tim Brown makes this point in Flexible Typesetting (2018), writ­ing: _”Your de­sign tool is work­ing against you. It is stuck in the tra­di­tional mind­set of ab­solute mea­sure­ments. This is pre­cisely one rea­son why peo­ple very good at web de­sign ar­gue that de­sign­ers should learn to write code. No main­stream de­sign tools […] are com­pletely ap­pro­pri­ate for the prac­tice of type­set­ting to­day.“9

To my knowl­edge this sit­u­a­tion has­n’t changed much since - so there’s plenty of room for ex­plo­ration. With bet­ter tools, con­tin­u­ous ty­pog­ra­phy might be­come more than a way to make the type look good on a phone: a new method for vi­sual ex­pres­sion in its own right. 10

Update February 2, 2021

I fi­nally got around to writ­ing a demo of what a de­sign tool for con­tin­u­ous ty­pog­ra­phy might look like - ba­si­cally a work­ing ver­sion of the fi­nal fig­ure above. Play with it here, or read more about here.

  1. Robert Bringhurst (2016): The Elements of Typographic Style, Version 4.2, Chapter 8. Hartley & Marks. ↩︎

  2. Pedro Rodriguez (2020): *Linearly Scale font-size with CSS clamp() Based on the Viewport ↩︎

  3. Jake Wilson (2017): CSS Poly Fluid Sizing us­ing calc(), vw, break­points and lin­ear equa­tions ↩︎

  4. In fact, the user’s de­fault font size should prob­a­bly be the first pa­ra­me­ter we care about. The only rea­son I’m us­ing the screen width here is that its ef­fects are eas­ier to vi­su­alise. ↩︎

  5. To my knowl­edge the ear­li­est de­scrip­tion of this tech­nique is a 2015 ar­ti­cle by Mike Riethmuller called Precise con­trol over re­spon­sive ty­pog­ra­phy ↩︎

  6. Greg Gibson (2020): Using CSS Custom Properties to Adjust Variable Font Weights in Dark Mode ↩︎

  7. Mike Riethmuller (2017): Non-linear Interpolation in CSS: A so­lu­tion for tran­si­tion­ing lengths val­ues in CSS through more than one bend­ing point. ↩︎

  8. Mike Riethmuller (2018): Interpolation in CSS with­out an­i­ma­tion ↩︎

  9. Tim Brown (2018): Flexible Typesetting, p 44. A Book Apart. ↩︎

  10. This post is also on Medium ↩︎