Responsive typography: A starting point

Responsive Typos

TL;DR  Responsive web design (RWD) requires that you not only have a flexible layout, but also font that responds to the device and sizes itself appropriately.  Font size that looks good on your desktop is going to be huge on a phone.  Setting your font-size in terms of pixels (px) is the norm, but it can be tedious to keep setting new font-sizes for every @media query for every font element.  Expressing your font-sizes in ‘rem’ and having a good sass mixin will speed up your development.  Using rem-units is an awesome technique, but how great you think it it is will depend on how much minute control you want to be exercising for every selector at every device width within your css.

What is responsive typography?

In a nut shell, having responsive typography means your web pages’ font sizes and spacing will adapt to the various viewport sizes in which they are displayed. The size of the font characters, their line-height, and their spacing from other page elements can and should be changed on larger and smaller devices. The font sizes you set while building your site look great on your 27-inch monitor – but they are impractical when being seen on a handheld device.

long-scroll

Typography is an art just as much as it is a science. This post is just to be an introduction to responsive typography and some tips on how to integrate it into your workflow. If you would like a more in-depth discussion of the topic, I highly recommend Oliver Reichenstein’s post Responsive Typography: The Basics as your starting point.

Responsive typography is another aspect of responsive web design (RWD) where you will be giving consideration to a site’s content – and not just its grid-based layout. Another realm where you are creating responsive content is responsive images – you can check out my introductory post to responsive images here.

Less theory; more action

Demonstration of responsive typography on CodePen

In my Pen above, you can see that the font-sizes are changing at different viewport sizes. I’m using the Bootstrap grid layout, and I have essentially a font-size for large devices (greater 1200px), medium devices (1200px ~ 992px), small devices (992px ~ 768px) and extra-small devices (less than 768px). I’ve included my Pen, “Responsive Typos”, below so you can check out the code.

See the Pen Responsive Typos by Nathan Ferguson (@NathanPJF) on CodePen.0

At each @media query I’m not setting any new font-sizes. Even though my HTML includes an h1, h2, h3, p and li elements, my entire css styling for the a medium sized device is just one line:

 html { font-size: 62.5%; }

So what’s going on here? The are three main things:

  1. The units I am using to express my font-size are ‘rems’, not pixels.
  2. I am using a Sass @mixin to handle conversions from pixels to rems and to write my fallback for me automatically (more on that later).
  3. I am changing to the font-size of the html element at each media query.

Font-size units: Fixed vs. Scaleable

Font sizes can be expressed in a number of untils – some of which are fixed and other are scaleable.

Fixed unit sizes:

  • Pixels (px): A pixel is essentially “one dot” on your device’s screen. However, on the web when expressing font size in px, the pixel-density of your device is taken into consideration and adjusted accordingly. This is why a font-size of 16px will look the same on a retina and non-retina display.
  • Points (pt): Typically points are reserved for print media and and one “point” measures out to 1/72 of an inch. Points are seldom used in screen media, but if you’re curious: 1pt ~ 1.33px.

Scalable unit sizes:

  • Ems (em): A font expressed in em-units will scale in relation to the current font-size. For a an element whose font-size is currently equal to 16px: any elements nested inside it expressed as 1em will be equal to 16px on the screen, any nested elements expressed as 2em will be equal to 32px on the screen.
  • Percentage (%): Similar to ems, percentages are an expression of the current font size. In a page element where the font is 16px: nested elements expressed as 100% will display as 16px, any nested elements with font-size: 50% will display as 8px.
  • Root ems (rem): Similar to ems, however the font-size will scale in relation to the root’s font-size – the font-size of the <html> element. If the the HTML font size is 10px, and an element is 2rem it will display as 20px. There is no trickle-down effect on nested elements as the font is always scaling in relation to a constant data point at the root.

In the past, fixed unit font sizes used to be disadvantageous to the visually impaired because the font size would refuse to change as a user zoomed in on a web site’s content. Today, modern browsers have addressed that problem. Observe:

Demonstration of increasing and decreasing font size on the web by zooming in and out.

Why use a scaleable font unit?

If you want to be a cool dude/dudette and have the font size of your elements change for different browsers, *but you set all your font sizes in terms of px* then you are going to have a lot of tedious work ahead of you. At each media query, you will need to set write a new font-size for each your p, li, h1, h2, h3 that you set. Chances are that you also had some classes attached to some of those elements which meant h2 is a different font-size than h2.warning… which in turn is a different font-size than h2.promotion.

Writing a new pixel-based unit size for each font element and all its classes is going to become **very annoying**. You’ll want to use a font unit that’s a lot more flexible and going to do most of the work for you – this is why we made computers isn’t it?!

Using REM over EM

Using ‘em’ has become very popular for web development because it is a scaleable changes with respect to the parent element size. The difficulty with em units though is how big 1 em is will always be a moving target. As I show in a section of my CodePen “Responsive Typos”, the size of 1em is influenced by the size of it’s parent container and nested elements can start to get away on you quickly. Unlitmately, you could end up with a document where 0.5em in one container is the same size visually as 8em that is in a container right next to it… Sound confusing? It is!

As discussed earlier, ‘rem’ units scale in relation to the root element: the <html> element. You don’t have to worry about the future effects of nesting elements on the page if you choose to use ‘rem’. Ultimately this makes predicting the actual display size of your fonts when writing code a lot easier and won’t cause any frustrating domino effects to font-size when you decide to change a random element.

Domino towering falling, much sadness

Always have a fallback plan

Browser support for rem is not yet universal – but it’s reeeaallly close. So we are going to want to incorporate a fallback expressed in pixels for those browsers/devices that don’t support rem units. Thanks to the wonders of cascading style sheets, we just need to right our pixel units before our rem units. If a browser doesn’t know ‘rem’, it will just ignore it and use the pixel expression. Taken from my CodePen:

 p {
   font-size: 20px;
   font-size: 2rem;
 }

Writing out these fallbacks every time can be a pain, so I am going to use the power of Sass @mixins to do a lot of the heavy lifting for me.

Getting things done faster with Sass

I’ve written my styles not in vanilla css, but in a pre-proccesing language that makes Syntactically Awesome Stylesheets – Sass.  Sass is relatively new to the scene, Prior to it, the most common pre-processor on the scene was LESS. Today, it looks like Sass is overtaking LESS in terms of popularity. Why? Probably because it’s 20 times more fun to say you ‘work with sass’.

sass-glitterfy

An explanation of Sass and creating “sassy” style sheets (.scss) is outside the scope of this blog post, so I am just going to show you my workflow in a couple steps. If you want to know more about Sass checkout their official Sass Basics page.

Set base variables

First I set some “base variables” for font sizes of common elements. Notice that I *do not* include any units in these variables. The actual units will be assigned later in my mixins.

 $base-font-size: 16; //will translate to 16px when html font-size is 62.5%
 $h1-font-size: 40;
 $h2-font-size: 26;

Conversion @function

I am going to need a @function that will help me convert pixel sizes to be expressed in rem sizes. I use a simple formula that shifts the decimal place to left for any value given to it.

 @function rem-sizing($target-size){
   @return ($target-size / 10);
 }

@mixin with the fallback

With my @mixin titled ‘font-size’, I can pass any value and have it write me two declarations. The first is the pixel-based fallback and the second is the rem-based declaration. Now, the value I will be passing into it is always going to be the pixel display value I am going for on a desktop. This is a personal preference – it ultimately doesn’t matter since it is later in the CSS that will determine how big the font will be displayed. The important thing is to remain consistent in your development.

 @mixin font-size($font-size: $base-font-size) {
    font-size: ($font-size) + px;
    font-size: rem-sizing($font-size) + rem;
 }

As I said earlier, responsive typography is not just about font size, but also having adaptive line-heights and spacing around the text elements. I can reuse my @function ‘rem-sizing’ to make some funky text-padding @mixins that will also be rem-based with a fallback.

 @mixin text-padding($text-padding: $base-pad-size, $pad2: false, $pad3: false, $pad4: false) {
    @if $pad4 {
      padding: ($text-padding) + px ($pad2) + px ($pad3) + px ($pad4) + px;
      padding: rem-sizing($text-padding) + rem rem-sizing($pad2) + rem rem-sizing($pad3) + rem rem-sizing($pad4) + rem;
    } @else if $pad3 {
      padding: ($text-padding) + px ($pad2) + px ($pad3) + px;
      padding: rem-sizing($text-padding) + rem rem-sizing($pad2) + rem rem-sizing($pad3) + rem;
    } @else if $pad2 {
      padding: ($text-padding) + px ($pad2) + px;
      padding: rem-sizing($text-padding) + rem rem-sizing($pad2) + rem;
   } @else {
     padding: ($text-padding) + px;
     padding: rem-sizing($text-padding) + rem;
   }
 }
 @mixin padding($side, $padAmount: $base-pad-size) {
   padding-#{$side}: ($padAmount) + px;
   padding-#{$side}: rem-sizing($padAmount) + rem;
 }

You’ll notice I didn’t make a @mixin for line-height. I do set different line-heights for different device-widths, but I like to keep my line-height expressed in em units.  In my opinion, the line-height of a font should be based on the size of the font itself, and not tied to any other variable.

Doing all this ground work in Sass means I can now write my sassy font-size declarations easier using one of my variables as the argument…

 h1{
   @include font-size($h1-font-size);
 }

Or by just passing an actual value to the argument which is super handy if you just want to right a quick “one-off” declaration.

 span{
   @include font-size(18);
 }

Changing the root with @media queries

Final step! A ‘rem’ unit will scale based on a constant root element, in this case the <html> element. However, that roots itself isn’t actually going to stay constant once we implement responsiveness to our typography. At different device sizes, we will grow and shrink the size of your root and all the elements associated with it will grow and shrink in proportion.

Because I’m using Bootstrap as a framework, I’m working with a “mobile first” approach in the order I write my @media queries. For mobile, I’ve set the font-size of the <html> element to be 45%.

 html{
   font-size: 45%;
 }

As the device gets bigger, I’m simply going to bump up the font-size of the <html> element.

// Small (sm) devices
 @media (min-width: 768px) {
   html{ font-size: 55%; }
 }
// Medium (md) devices
 @media (min-width: 992px) {
   html{ font-size: 62.5%; }
 }
// Large (lg) devices
 @media (min-width: 1200px) {
   html{ font-size: 72%; }
 }

Pics or it didn’t happen

First, here’s a side-by-side comparison of the <h1> element at all four of the different breakpoints specified in the @media queries.

Demonstration of REM font-sizing at progressively smaller font sizes.

A comparison of the <p> element at each of the four device sizes….

Demonstration of REM font-sizing at progressively smaller font sizes.

Rem units: A perfect solution… right?

I love using rem-units. It speeds up development and feels like a “set it and forget it” solution once you’ve gotten started. However, your @media queries are probably not going to look as simple as the ones we have in my examples. Inevitably, you’ll want certain elements to be juuuust a bit smaller/bigger at different device widths and changing that requires an additional selector declaration within that @media query’s CSS.

If you find that you are constantly overriding the font-sizes within your @media queries, there are a couple things you can explore:

  • You could try changing the <html> font-size to a different percentage a number of times and see if any of the newly render font sizes are what you are going for.
  • You could abandon rem units all together and express everything in fixed units at the different @media queries.
  • You could stick with rem font-sizing, but still make your desired changes for different elements at different @media queries.  This method has become my preferred way of working.

Regardless of what method you us to implement responsive typography, I want to give you props and say a big THANK YOU! We’re all working towards a more beautiful web.

Leave a Comment.