Between the Lines

Media queries are great for changing values in sudden snaps at different screen sizes. But, combining the power of calc() and viewport units like vw and vh , we can get an awful lot of fluidity across our layouts. For this we'll use a technique called linear interpolation.

Linear interpolation is a formula used to find a value between two points on a line. In our case those two points are CSS values, like font-sizes, margins or widths, that we want to interpolate between over a set of viewport widths.

The reason we might want to interpolate between values over a set of viewport widths is to avoid having to create multiple breakpoints to control the flow of our content when the viewport changes. Instead, we let the user's browser calculate, according to our instructions, what values it gets. Let me explain.

The following snippet is a Sass function based on linear interpolation that we, at Aranja, have been calling between.

/**
 * Computes a CSS calc function that betweens a value from
 * A to B over viewport-width A to viewport-width B.
 * Requires a media query to cap the value at B.
 */

@function between($from, $to, $fromWidth, $toWidth) {
  $slope: ($to - $from) / ($toWidth - $fromWidth);
  $base: $from - $slope * $fromWidth;

  @return calc(#{$base} + #{100vw * $slope});
}

The function is used like so:

$small: 400px; 
$large: 1000px;

.Container {
  /* The base (smallest) value. */
  padding: 20px;

  /* In $small it should be 20px and in $large it should be 100px,  */
  /* In viewports between that its padding should be calculated */
  @media (min-width: $small) {
    padding: between(20px, 100px, $small, $large);
  }

/* In $large we cap the value at 100px */
  @media (min-width: $large) {
    padding: 100px;
  }
}

The code example above shows how we can between a container's padding from being 20px in “mobile”-size to being 100px in “desktop”-size. Any size between that would get a calculated amount of padding ranging between 20 and 100 pixels. To prevent the padding from exceeding the maximum value we cap it with a breakpoint.

Try resizing the following demo to see the example in action:

See the Pen

The between function excels in solving spacing problems. Problems that previously we would have solved by hand using different media queries.

Example layout

The talented Carly Sylvester lent me the following wireframe she designed for a volunteer firefighter website so I get to demonstrate this technique on a real-world layout.

The design document consists of a desktop, tablet and mobile versions of the website, at 1440px, 720px and 324px respectively.
In reality, the lines between these different devices are not so clear-cut, so we'll use the given values as target points and interpolate between them using our function.

Notice how the layout of the desktop and tablet versions are similar except for the use of white-space and font-sizes–which we'll be able to interpolate between smoothly. When we get to our smallest target point we snap the layout into a classic full-width mobile layout.

See the Pen

For comparison, let's see the implementation done the usual way, where the 3 main breakpoints are used to snap the layout their new values.

See the Pen

Videos of Demos

Resizing layout with between
Resizing layout without between

Try browsing the demos in different screen sizes and the benefits of our function should be clear. To make the "regular" version of the website better we'd need to add more breakpoints in addition to the original three from the design document, resulting in more jumps.

Combined with the power of rem

A powerful technique we can do with the function is to between the root font-size and base our styles on rems:

See the Pen

The rem unit refers to the root font-size (the font size set on the :root or html element) to determine its value and the root font-size refers to the viewport width to determine its value. We set the root font-size to be 10px at the smallest and 18px at the largest and the result is a smooth transition between the two. The smallest the card in the demo ever gets is 245px, at our $small breakpoint, and it gradually grows until it reaches its peak, 440px, at our $large breakpoint.

Closing

I am convinced that by utilizing this technique we can make the web a better experience for users browsing on devices that don’t fit into the generalization of "mobile" or "tablet" sizes. For me, at least, it also makes the development process more enjoyable as there are fewer layout-related bugs on my table.

Regarding browser support–the fact that we only need calc() and vw for the technique to work puts it at around 97% in USA and Europe, and around 84% globally.

In the demos above, for example, unsupported browsers (Which are mainly Opera Mini and UC Browser) will either use our lowest (base) values or our highest (capped) values, depending on the viewport width.