CSS Conf EU Berlin, 31 May, 2019

Dynamic Typographic Systems &
Variable Fonts

Scalable, Fast, and Fabulous

https://rwt.io | @jpamental
Itinerant typographer,
Walker of collies

Type is the voice of our words

It’s how we ‘hear’ what we read

“There are no crystal goblets, no defaults devoid of friction. Design has visible surfaces, inevitably, and they brim with significance and context and connotation and intent and tone”

—Nina Stössinger

A new chapter, about three new things

The Odyssey, Book I

Tell me, O muse, of that ingenious hero who travelled far and wide after he had sacked the famous town of Troy. Many cities did he visit, and many were the nations with whose manners and customs he was acquainted; moreover he suffered much by sea while trying to save his own life and bring his men safely home; but do what he might he could not save his men, for they perished through their own sheer folly in eating the cattle of the Sun-god Hyperion; so the god prevented them from ever reaching home. Tell me, too, about all these things, O daughter of Jove, from whatsoever source you may know them.


  .this-heading {
    --heading-color: #212171;
  }


  .this-heading.changed {
    --heading-color: #914141;
  }


  .this-heading {
    --heading-size: 5;
    font-size: calc( var(--heading-size) * 1vw );
  }


  .this-heading {
    --heading-size: 5;
    font-size: calc( var(--heading-size) * 1vw );
  }
  .this-heading.changed {
    --heading-size: 6;
  }


  .this-heading {
    --heading-weight: 700;
    --heading-width: 100;
    font-weight: var(--heading-weight);
    font-stretch: calc( var(--heading-width) * 1% );
  }


  .this-heading.thinner {
    --heading-weight: 150;
    --heading-width: 80;
  }

The Odyssey, Book I

Tell me, O muse, of that ingenious hero who travelled far and wide after he had sacked the famous town of Troy. Many cities did he visit, and many were the nations with whose manners and customs he was acquainted; moreover he suffered much by sea while trying to save his own life and bring his men safely home; but do what he might he could not save his men, for they perished through their own sheer folly in eating the cattle of the Sun-god Hyperion; so the god prevented them from ever reaching home. Tell me, too, about all these things, O daughter of Jove, from whatsoever source you may know them.

A little bit

Tell me, O muse, of that ingenious hero who travelled far and wide after he had sacked the famous town of Troy.

Can go farther

Many cities did he visit, and many were the nations with whose manners and customs he was acquainted

Than one expects

Moreover he suffered much by sea while trying to save his own life and bring his men safely home


  .small-card {
    --h3-size: 3;
    --p-size: 1.6;
  }


  .sidebar .small-card {
    --h3-size: 2.5;
    --p-size: 1.5;
  }

You might be asking

What was that strange magic in Slide 24?

A variable font is a single font file that behaves like multiple fonts

—John Hudson

a
a
a
a
a
a
a
a
a
a
a
a
a
a
a
width
width
weight
weight
x-height
x-height

  .width.super-floof {
    font-stretch: 140%;
  }
  .width.petite-puffle {
    font-stretch: 50%;
  }

  .weight.chonky-boi {
    font-weight: 900;
  }
  .weight.skinny-minnie {
    font-weight: 500;
  }

  .x-height.bubba-bear {
    font-variation-settings: 'YTLC' 600;
  }
  .x-height.lil-bit {
    font-variation-settings: 'YTLC' 450;
  }
slant

  .slant.naptime {
    font-style: oblique 12deg;
  }

Scaling a scale


  :root {
    /* breakpoint variables */
    --bp-small: 25;
    --bp-medium: 45;
    --bp-large: 55;
    --bp-xlarge: 65;
  
    /* set scale values */
    --p-size-min: 1;
    --p-size-mid: 1.125;
    --p-size-max: 1.25;
    --p-lh-min: 1.2;
    --p-lh-mid: 1.4;
    --p-lh-max: 1.6;
  }

[Set font-size & line-height vars]

  p {
    font-size: calc( var(--p-size-min) * 1em );
    line-height: var(--p-lh-min);
  }

[Scale font-size & line-height]

  @media screen and (min-width: 25em) {
    p {
      line-height: calc(( var(--p-lh-min) * 1em ) + 
        ( var(--p-lh-max) - var(--p-lh-min) ) * 
        ((100vw - ( var(--bp-small) * 1em )) / 
        ( var(--bp-xlarge) - var(--bp-small) )));
      font-size: calc(( var(--p-size-min) * 1em ) + 
        ( var(--p-size-max) - var(--p-size-min) ) * 
        ((100vw - ( var(--bp-small) * 1em )) / 
        ( var(--bp-xlarge) - var(--bp-small) )));
    }
  }
  @media (min-width: 65em) {
    p {
      font-size: calc( var(--p-size-max) * 1em );
      line-height: var(--p-lh-max);
    }
  }

calc( [size-min]em + ( [size-max] - [size-min] ) * ((100vw - [start]em ) / ( [stop] - [start] )));

“Please scale from 2.25em on small screens up to 5em on largest ones, starting at viewport width of 25em and stopping at 75em or larger”

calc( 2.25em + ( 5 - 2.25 ) * ((100vw - 25em ) / ( 75 - 25 )));

calc( 2.25em + ( 2.75 ) * ((100vw - 25em ) / ( 50 )));

Putting it all together


  :root {
    /* breakpoint variables */
    --bp-small: 25;
    --bp-medium: 45;
    --bp-large: 55;
    --bp-xlarge: 65;
  
  /* initial variable font values */
    --text-wght: 375;
    --text-wdth: 95;
    --text-opsz: 16;
    --text-GRAD: 0;
    
    /* set scale values */
    --p-size-min: 1;
    --p-size-mid: 1.125;
    --p-size-max: 1.25;
    --p-lh-min: 1.2;
    --p-lh-mid: 1.4;
    --p-lh-max: 1.6;
    --p-wdth-min: 85;
    --p-wdth-mid: 92.5;
    --p-wdth-max: 100;
  }


  p {
    --text-wght: 400;
    --text-GRAD: -0.1;
    font-size: calc( var(--p-size-min) * 1em );
    font-stretch: calc( var(--p-wdth-min) * 1% );
    font-weight: var(--text-wght);
    font-variation-settings: 
      'opsz' var(--text-opsz), 
      'GRAD' var(--text-GRAD);
    line-height: var(--p-lh-min);
  }


  @media screen and (min-width: 25em) {
    p {
      line-height: calc(( var(--p-lh-min) * 1em ) + 
        ( var(--p-lh-max) - var(--p-lh-min) ) * 
        ((100vw - ( var(--bp-small) * 1em )) / 
        ( var(--bp-xlarge) - var(--bp-small) )));
      font-size: calc(( var(--p-size-min) * 1em ) + 
        ( var(--p-size-max) - var(--p-size-min) ) * 
        ((100vw - ( var(--bp-small) * 1em )) / 
        ( var(--bp-xlarge) - var(--bp-small) )));
    }
  }
  @media (min-width: 65em) {
    p {
      --text-opsz: 20;
      font-size: calc( var(--p-size-max) * 1em );
      font-stretch: calc( var(--p-wdth-max) * 1% );
      line-height: var(--p-lh-max);
    }
  }

“The role of the typographer has changed. We no longer decide; we make suggestions”

—Tim Brown


  :root {
    /* default light mode colors */
    --color-bg: #f9f9fd;
    --color-text: #414141;
  
    /* initial variable font values */
    --text-GRAD: -0.1;
  }


  @media (prefers-color-scheme: dark) {
    --color-text: #f9f9fd;
    --color-bg: #414141;
    p {
      --text-GRAD: 0.25;
      letter-spacing: 0.015em;
    }
  }


  :root {
    /* default light mode colors */
    --color-bg: #f9f9fd;
    --color-text: #414141;
  
    /* initial variable font values */
    --text-GRAD: -0.1;
  }


  .contrast {
    /* higher contrast colors */
    --color-bg: #fafafe;
    --color-text: #212121;
  }
  .contrast p {
    --text-GRAD: 0.75;
    letter-spacing: 0.005em;
  }


  .bigtext {
    --p-size-min: 1.25;
    --p-size-mid: 1.375;
    --p-size-max: 1.5;
  }


  .spacing {
    --p-lh-min: 2.2;
    --p-lh-mid: 2.4;
    --p-lh-max: 2.6;
  }
  .spacing p {
    word-spacing: 0.75em;
  }


.teaser {
  grid-column: auto / span 1;
  --p-lh-min: 1.1;
  --p-lh-mid: 1.2;
  --p-lh-max: 1.3;
}
@media (min-width: 67em) {
  .teaser {
    --p-size-min: 0.875;
    --p-size-mid: 0.875;
    --p-size-max: 1;
  }
}


:root {
  /* font stack */
  --font-stack: "Roboto Delta", sans-serif;
  --font-stack-serif: "Gimlet VF", serif;
}
.theme-alt {
  font-family: var(--font-stack-serif);  
}
.theme-alt p {
  --text-wght: 400;
}
@media screen and (min-width: 25em) {
  .theme-alt p {
    --text-wght: 375;
}
@media screen and (min-width: 45em) {
  .theme-alt p {
    --text-wght: 350;
  }
}
@media screen and (min-width: 55em) {
  .theme-alt p {
    --text-wght: 330;
  }
}

IBM Plex Sans Variable
transform
16 files
960kb
into
2 files
233kb
!

Cast of Characters

FF Meta Variable

by Monotype

Input Mono Variable

by DJR

Jost* Variable

by indestrictible type*

Roboto Delta Variable

by Google & Type Network

Roslindale

by DJR

Roslindale Italic

by DJR

Source Serif Variable

by Adobe

Source Sans Variable

by Adobe

Proxima Nova Variable BETA

by Mark Simonson

Amstelvar

by Type Network

Plex Sans Variable

by IBM

Plex Sans Italic Variable

by IBM

thank you

Jason Pamental

  • https://rwt.io | @jpamental
  • https://rwt.io/newsletter
  • https://noti.st/jpamental/2gsS6v