Viewport sized typography with minimum and maximum sizes

Viewport units for typography are quite a cool toy to have in your responsive web design toolbox, as they allow you to size fonts relatively to the dimensions of the viewport. If you’ve never used them before, Chris Coyier’s article is probably a good place to start.

These units can produce really interesting results, but they must be used with caution. From my experience, there’s always a point where the font becomes unreadable on small screens, and sometimes too big on large screens. I end up setting a couple of media queries to set some boundaries on where the viewport units kick in.

As a result, I created a Sass mixin that abstracts what I wish you could more naturally in CSS: specify a minimum and a maximum size for the font while still using viewport based units. The mixin takes the viewport based size, a minimum value (in pixels), an optional maximum value (in pixels as well) and an optional fallback value, in whatever units you prefer, for browsers that don’t support viewport units.

///
/// Viewport sized typography with minimum and maximum values
///
/// @author Eduardo Boucas (@eduardoboucas)
///
/// @param {Number}   $responsive  - Viewport-based size
/// @param {Number}   $min         - Minimum font size (px)
/// @param {Number}   $max         - Maximum font size (px)
///                                  (optional)
/// @param {Number}   $fallback    - Fallback for viewport-
///                                  based units (optional)
///
/// @example scss - 5vw font size (with 50px fallback), 
///                 minumum of 35px and maximum of 150px
///  @include responsive-font(5vw, 35px, 150px, 50px);
///
@mixin responsive-font($responsive, $min, $max: false, $fallback: false) {
  $responsive-unitless: $responsive / ($responsive - $responsive + 1);
  $dimension: if(unit($responsive) == 'vh', 'height', 'width');
  $min-breakpoint: $min / $responsive-unitless * 100;
  
  @media (max-#{$dimension}: #{$min-breakpoint}) {
    font-size: $min;
  }
  
  @if $max {
    $max-breakpoint: $max / $responsive-unitless * 100;
    
    @media (min-#{$dimension}: #{$max-breakpoint}) {
      font-size: $max;
    }
  }
  
  @if $fallback {
    font-size: $fallback;
  }
  
  font-size: $responsive;
}

Caveat: vw or vh units work, vmin or vmax don’t.

Here’s a pen showing the effect.

See the Pen Viewport sized typography with minimum and maximum values by Eduardo Bouças (@eduardoboucas) on CodePen.

What are your thoughts? ∎

Comments

Lorenzo

Wednesday, June 24th, 2015, 07.47am

Thank you for this helpful and inspiring mixin! However, I think sizing fonts relatively to the dimensions of the viewport could be a problem with accessibility. It doesn't allow to increase the font size using the shortcut of the browser (cmd/ctrl +)

Igi Manaloto

Wednesday, June 24th, 2015, 01.23pm

Gave this a try. I've never really used vw/vh units except for this one odd project I was doing... This is quite an interesting approach! I'll try this first thing tomorrow.

Those who are using it, make sure you specify a pixel, percentage, em or rem size beforehand so unsupported or partially browsers (IE cough cough cough cough cough cough) can cope: http://caniuse.com/#feat=viewport-units

So thanks, hope to read more from you!

Thomas

Wednesday, June 24th, 2015, 05.02pm

Be careful with this: user-initiated zoom (ctrl/cmd + +) will NOT size the font, as the viewport stays the same, because we are using viewport units here. That's a huge accessibility issue to consider before using this. Otherwise, this is awesome :-)

Alex

Monday, September 12th, 2016, 11.47am

Hi there,

I was trying to use the code you provide but the text comes up in a bigger font size when viewed on laptops screen. When resizing the browser or on mobile devices, works fine.

The text is at the bottom of the front page.

have you any idea on how to fix this issue?

Regards,

Alex

Mark

Thursday, March 29th, 2018, 08.08am

I think we have to wait for CSS4 min/max This max-width/min-width nonsense is specific to device-specific balance between max/min px value and global viewport measure value. Can’t avoid size jumps, not fluid. I found a better way to fall into the “de-viced” trap, by forcing 1px non-fluid jump. TRY THIS /* viewport manipulation for device and presentation formats body { font-size:1.2vw; @media (max-width:1200px) { body { font-size: 16px; font-max-size: 24px; @media (min-width:1201px) { body { font-max-size: 16px; font-max-size: 64px;

Mark

Thursday, March 29th, 2018, 10.42am

So my display ‘fakes’ fluid steps with this balance ratio between width (1200/1600) and font-size(1.2vw/1.0vw)…

body{font-size:1.2vw;} and @media(max-width:1200px){body{font-size:16px}} and @media(min-width:1201px{body{font-max-size:16px}}…

incrementally reduces viewport font size increase with…

body{font-size:1.0vw;} and @media(max-width:1600px){body{font-size:16px}} and @media(min-width:1601px{body{font-max-size:16px}}…

So, this would define the CSS width ratio between viewport font-size and browser window px width as fixed. Mixin and script can morph that fixed ratio across mutiple ‘screen’ widths using ‘responsive’ arguments. To morph using Cascading Style Sheet, I say again, I think we have to wait for CSS4, though for now “font-max-size” shows a glimpse of what’s to come.

https://www.w3.org/TR/css-fonts-4/#font-style-matching

Mark

Thursday, March 29th, 2018, 11.20am

Andrew: on “Matching Font Styles”, read item 6 in above link.

font-size must be matched within a UA-dependent margin of tolerance. (Typically, sizes for scalable fonts are rounded to the nearest whole pixel, while the tolerance for bitmapped fonts could be as large as 20%.) Further computations, e.g., by em values in other properties, are based on the font-size value that is used, not the one that is specified.

I use Safari, where there is no ‘margin of tolerance’ for “FONT-MIN-SIZE”. In my two examples above, we can use fake fluidity to set a fixed font-size-min:16px and then when we resize above 1200px or 1600px, the fluid vm measure happens. Font size grows bigger, faster in my first post above, no idea why. something to do with fudged tolerance, that for now nixes your rem, em (and ex) units of measure.

Leave a comment