Jake Wilson

About The Author

Software Developer, husband and father of three
LinkedInStackOverflowGitHubMediumTwitter More about Jake

Fluid Responsive Typography With CSS Poly Fluid Sizing

Quick Summary

Fluid layouts have been a normal part of front-end development for years. The idea of fluid typography, however, is relatively new and has yet to be fully explored. Up until now, most developers’ idea of fluid typography is simply using Viewport units maybe with some minimum and maximum sizes.

In this article, we are going to take it to another level. We are going to examine how to create scalable, fluid typography across multiple breakpoints and predefined font sizes using well-supported browser features and some basic algebra. The best part is that you can automate it all by using Sass.

Table of Contents

    Fluid layouts have been a normal part of front-end development for years. The idea of fluid typography, however, is relatively new and has yet to be fully explored. Up until now, most developers’ idea of fluid typography is simply using Viewport units maybe with some minimum and maximum sizes.

    In this article, we are going to take it to another level. We are going to examine how to create scalable, fluid typography across multiple breakpoints and predefined font sizes using well-supported browser features and some basic algebra. The best part is that you can automate it all by using Sass.

    Further Reading on SmashingMag:

    When working with creative designers on web page designs, it’s fairly common to receive multiple Sketch or Photoshop artboards/layouts, one for each breakpoint. In that design, elements (like an h1 heading) will usually be different sizes at each breakpoint. For example:

    1. The h1 at the small layout could be 22px
    2. The h1 at the medium layout could be 24px
    3. The h1 at the large layout could be 34px

    The bare minimum CSS for this uses media queries:

    h1 {
      font-size: 22px;
    }
    @media (min-width:576px) {
      h1 {
        font-size: 22px;
      }
    }
    @media (min-width:768px) {
      h1 {
        font-size: 24px;
      }
    }
    @media (min-width:992px) {
      h1 {
        font-size: 34px;
      }
    }

    This is a good first step, but you are limiting the font-size to only what was specified by the designer at the provided breakpoints. What would the designer say if you asked, “What should the font-size be at an 850px wide viewport?” The answer in most cases is that it would be somewhere between 24px and 34px. But right now, it’s just 24px according to your CSS, which is probably not what the designer was envisioning.

    Your option at this point is to calculate what that size should be and add another breakpoint. That’s easy enough. But what about all the other resolutions? What should the font-size be at 800px wide? What about 900px? What about 935px? Obviously the designer is not going to provide you with a full layout for every single possible resolution. Even if they did, should you be adding dozens (or hundreds) of breakpoints for all the different font-sizes that are desired by the designer? Of course not.

    Your layout is already fluidly scaling with the width of your viewport. Wouldn’t it be nice if your typography predictably scaled with your fluid layout? What else can we do to improve upon this?

    Viewport Units To The Rescue?

    Viewport units are another step in the right direction. They allow your text to fluidly resize with your layouts. And the browser support is great these days.

    Can I Use Viewport?
    (View large version)

    But the viability of Viewport units is very dependent on the original creative designs for a web page. It would be great to just set your font-size using vw and be done:

    h1 {
      font-size: 2vw;
    }
    

    But this only works if your creative art-boards take this into account. Did the designer choose a text size that was exactly 2% of the width of each of his art-boards? Of course not. Let’s calculate what the vw value would need to be for each of our breakpoints:

    22px size @ 576px wide = 22576*100 = 3.82vw 24px size @ 768px wide = 24768*100 = 3.13vw 34px size @ 992px wide = 34992*100 = 3.43vw

    They are close but they aren’t all the same. So you would still need to use media queries to transition between text sizes and there would still be jumps. And consider this weird side-effect:

    @ 767px, 3.82% of the viewport width is 29px. If the viewport is 1-pixel wider the font-size sudden drops back down to 24px. This animation of a viewport being resized demonstrates this undesirable side effect:

    This dramatic change in font-size is almost definitely not what the designer was envisioning. So how do we solve this problem?

    Statistical Linear Regression?

    Wait. What? Yes, this is an article about CSS, but some basic math can go a long way towards an elegant solution to our problem.

    First, lets plot our resolutions and corresponding text sizes on a graph:

    Scatter plot of font-size and corresponding Viewport width
    Scatter plot of font-size and corresponding Viewport width (Google Spreadsheets) (View large version)

    Here you can see a scatter plot of the designer’s specified text sizes at the defined viewport widths. The x-axis is the viewport width and the y-axis is the font-size. See that line? That’s called a trendline. It’s a way to find an interpolated font-size value for any viewport width, based on the data provided.

    The Trendline Is The Key To All Of This

    If you could set your font-size according to this trendline, you would have an h1 that smoothly scales on all resolutions that would come close to matching what the designer intended. First, let’s look at the math. The straight line is defined by this equation:

    Linear equation definition
    Linear equation definition
    • m = slope
    • b = the y-intercept
    • x = the current viewport width
    • y = the resulting font-size

    The are several methods for determining the slope and y-intercept. When multiple values are involved, a common method is the Least Squares fit:

    Least Squares

    Once you run those calculations, you have your trendline equation.

    How Do I Use This In CSS?

    Okay, this is getting pretty heavy on the math. How do we actually use this stuff in front-end web development? The answer is CSS calc()! Once again, a fairly new CSS technology that is very well supported.

    Can I Use Calc?
    (View large version)

    You can use the trendline equation like this:

    h1 {
      font-size: calc({slope}*100vw + {y-intercept}px);
    }

    Once you find your slope and y-intercept you just plug them in!

    Note: You have to multiply the slope by 100 since you are using it as a vw unit which is 1/100th of the Viewport width.

    Can This Be Automated?

    I ported the least squares fit method into an easy-to-use Sass function:

    /// least-squares-fit
    /// Calculate the least square fit linear regression of provided values
    /// @param {map} $map - A Sass map of viewport width and size value combinations
    /// @return Linear equation as a calc() function
    /// @example
    ///   font-size: least-squares-fit((576px: 24px, 768px: 24px, 992px: 34px));
    /// @author Jake Wilson <jake.e.wilson@gmail.com>
    @function least-squares-fit($map) {
      
      // Get the number of provided breakpoints
      $length: length(map-keys($map));
      
      // Error if the number of breakpoints is < 2
      @if ($length < 2) {
        @error "leastSquaresFit() $map must be at least 2 values"
      }
        
      // Calculate the Means
      $resTotal: 0;
      $valueTotal: 0;
      @each $res, $value in $map {
        $resTotal: $resTotal + $res;
        $valueTotal: $valueTotal + $value;
      }
      $resMean: $resTotal/$length;
      $valueMean: $valueTotal/$length;
    
      // Calculate some other stuff
      $multipliedDiff: 0;
      $squaredDiff: 0;
      @each $res, $value in $map {
        
        // Differences from means
        $resDiff: $res - $resMean;
        $valueDiff: $value - $valueMean;
        
        // Sum of multiplied differences
        $multipliedDiff: $multipliedDiff + ($resDiff * $valueDiff);
        
        // Sum of squared resolution differences
        $squaredDiff: $squaredDiff + ($resDiff * $resDiff);
      }
    
      // Calculate the Slope
      $m: $multipliedDiff / $squaredDiff;
    
      // Calculate the Y-Intercept
      $b: $valueMean - ($m * $resMean);
    
      // Return the CSS calc equation
      @return calc(#{$m*100}vw + #{$b});
    
    }

    Does this really work? Open up this CodePen and resize your browser window. It works! The font sizes are fairly close to what the original design was asking for and they smoothly scale with your layout.


    More Articles on

    How To Create Native Cross-Platform Apps With Fuse

    by Wern Ancheta

    Fuse is a toolkit for creating apps that run on both iOS and Android devices. It enables you to create apps using UX Markup, an XML-based language. But unlike the components in React Native and NativeScript, Fuse is not only used to describe the UI and layout; you can also …

    Read more

    An Abridged Cartoon Introduction To WebAssembly

    by Lin Clark

    There’s a lot of hype about WebAssembly in JavaScript circles today. People talk about how blazingly fast it is, and how it’s going to revolutionize web development. But most conversations don’t go into the details of why it’s fast. In this article, I …

    Read more

    An Abridged Cartoon Introduction To WebAssembly

    by Lin Clark

    There’s a lot of hype about WebAssembly in JavaScript circles today. People talk about how blazingly fast it is, and how it’s going to revolutionize web development. But most conversations don’t go into the details of why it’s fast. In this article, I …

    Read more