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;
}