Kermode

/ˈkɜːrmoʊd/
The Spirit Bear

Written by Gray Gilmore

I make websites for a living. I love CSS and building simple and modular stylesheets. Somewhere along the way I also turned into a Ruby developer. Life comes at you fast I guess. You can read my resume to learn about my journey so far.

You can find me as @graygilmore on several platforms:

Good ol' fashion email works, too: hello@kermode.co

You can subscribe to this blog via RSS: XML, JSON

Responsive Font Sizing with Min and Max

Responsive font sizing has been all the rage since viewport units became widely supported in modern browsers. The idea is to use viewport units (vw, vh, vmin, vmax) as your font-size value to automatically scale.

body {
  font-size: 3vw;
}

The issue with this approach is that you could end up with sizes that are super small or super large. CSS doesn’t have font-size-min or font-size-max properties but we do have media queries and our good friend: math.

Math

Do you remember your high school math? If you were paying attention you might remember a formula called y = mx + b. I’m not going to describe what that does here but feel free to take a break to read more about linear equations.

Over on Mike Riethmuller’s blog we can see exactly how to use this in our Sass:

font-size: calc( 12px + (24 - 12) * ( (100vw - 400px) / (800 - 400) ));

In the formula above we’ve manually entered the minimum font size to be 12px, the maximum font size to be 24px and for it to scale between 400px and 800px viewport widths.

Mixin

After Mike’s post the power of the Internet took over and it wasn’t long before Indrek Paas created a Sass mixin that makes this super easy to use. Usage looks like this:

body {
  @include fluid-type(320px, 1280px, 14px, 18px);
}

One thing I don’t like about this is that you are required to supply min and max values for the viewport. I added some default values and added some fallback warnings if you provide invalid arguments. Now you can use it like this:

body {
  @include fluid-type(14px, 18px);
}

You can also pass in named arguments if you want to override any of the defaults:

h1 {
  @include fluid-type(20px, 32px, $vw-min: 500px);
}

Cool! You can view a demo on CodePen to see this live in action. Below is my forked and modified version of Indrek’s mixin (with additional documentation to boot):

/// Responsive font sizing. The size of the font will scale with the viewport
/// based on the information that you provide it. Additional properties may be
/// adding for things like padding.
///
/// @param {Integer} $font-size-min
/// @param {Integer} $font-size-max
/// @param {String} $properties [font-size]
/// @param {Integer} $vw-min [320] - the minimum viewport width
/// @param {Integer} $vw-max [1280] - the maximum viewport width
///
/// @require {function} strip-unit
///
/// @example scss - Basic usage Sass
///   h1 {
///     @include fluid-type(28px, 52px);
///   }
///
/// @example scss - Basic usage CSS output
///   h1 {
///     font-size: 28px;
///   }
///
///   @media screen and (min-width: 320px) {
///     h1 {
///       font-size: calc(28px + 24 * (100vw - 320px) / 960);
///     }
///   }
///
///   @media screen and (min-width: 1280px) {
///     h1 {
///       font-size: 52px;
///     }
///   }
///
/// Concept of precise control from Mike Riethmuller
/// https://madebymike.com.au/writing/precise-control-responsive-typography/
///
/// Original mixin implementation from Indrek Paas
/// http://www.sassmeister.com/gist/7f22e44ace49b5124eec
@mixin fluid-type(
  $font-size-min,
  $font-size-max,
  $properties: font-size,
  $vw-min: 320px,
  $vw-max: 1280px
  ) {

  @if(
    unitless($font-size-min) or
    unitless($font-size-max) or
    unitless($vw-min) or
    unitless($vw-max)
  ) {
    @error "All min and max properties must be provided with a unit (e.g.: px)";
  }

  @if(
    unit($font-size-min) == unit($font-size-max) and
    unit($font-size-min) == unit($vw-min) and
    unit($font-size-min) == unit($vw-max)
  ) {
    @each $property in $properties {
      #{$property}: $font-size-min;
    }

    @media screen and (min-width: $vw-min) {
      @each $property in $properties {
        #{$property}: calc(#{$font-size-min} + #{strip-unit($font-size-max - $font-size-min)} * (100vw - #{$vw-min}) / #{strip-unit($vw-max - $vw-min)});
      }
    }

    @media screen and (min-width: $vw-max) {
      @each $property in $properties {
        #{$property}: $font-size-max;
      }
    }
  } @else {
    @error "All units must be the same. Incompatible units were passed:
    #{unit($font-size-min)}, #{unit($font-size-max)}, #{unit($vw-min)},
    #{unit($vw-max)}";
  }
}

/// Remove the unit of a length
/// @param {Number} $number - Number to remove unit from
/// @return {Number} - Unitless number
///
/// Original implementation by Miriam Suzanne
/// http://stackoverflow.com/a/12335841/1339786
///
/// Update implementation from Hugo Giraudel
/// https://css-tricks.com/snippets/sass/strip-unit-function/
@function strip-unit($number) {
  @if type-of($number) == 'number' and not unitless($number) {
    @return $number / ($number * 0 + 1);
  }

  @return $number;
}

Resources