Here’s a quick SASS mixin for developers using Bootstrap 4.  Note that it only works for styles with a single value.

The Motivation….

I was inspired to write this because I was working on a new custom WordPress theme where I had design mockups for Desktop and Mobile, which is great!  The challenge was there was really a big difference between the Desktop and Mobile versions.  The Desktop mockup depicted a screen width of 1920px, and the mobile version was for a width of 375px.

The execution was really going to call for a LOT of interstitial styles, so I wanted a handy way to take the well-defined high and low values, and let it fill in the middle bits for me automatically.

Equal Interval Interpolation (quick and dirty)

@mixin interpolate-style( $style, $smallest, $largest ) {

  $interval: ( $largest - $smallest ) / ( length($grid-breakpoints) - 2 );
  $idx: -1;

  #{$style}: $smallest;

  @each $key, $breakpoint in $grid-breakpoints {

    // skip the first two keys
    @if $idx > 0 {
      @include media-breakpoint-up($key) {
        #{$style}: $smallest + ( $idx * $interval );
      }
    }

    $idx: $idx + 1;

  }

}

 

It takes the high and low values for a style, and defines the middle ones for you, breaking them into equal intervals.

By equal interval, we mean the algorithm assumes the differences between each value should be equal.  For example, if your highest value is 160px and lowest is 40px, and you want 4 values (including the given high and low), they would be assigned at 40px intervals:

  • 40px (given lowest)
  • 80px
  • 120px
  • 160px (given highest)

Note that we are ignoring the lowest value, since in my case, I didn’t want the styles to start changing until the 2nd breakpoint:

  • xs: 0
  • sm: 40px (given lowest)
  • md: 80px
  • lg: 120px
  • xl: 160px (given highest)

Notice that there’s a bit of a flaw in the logic in the above case, though, since it doesn’t take into account the actual intervals between breakpoint widths. It implies the breakpoint widths are always going to be equal distances apart, and that’s probably not going to be the case.  This might work if you just want something quick and dirty, but if you want something more “correct,” you’ll want to use a linear interpolation.

Linear Interpolation (Better!)

What we’re going to do is say there is a line defined by the lowest and highest values and their breakpoint widths, and then we’ll take the widths for the other points, and calculate their values based on the slope of the line and the y-intercept.  Yup… good ole algebra to the rescue.

Remember the equation for a line?

Y = mX + b

Where m is the slope, and b is the y-intercept.

I’m going to assume a few things here…

  1. $grid-breakpoints is the map of breakpoint values.
  2. We don’t know how many breakpoints there are
  3. We don’t know the key values
  4. We assume the breakpoints are listed in order from lowest screen width to highest
  5. The first value will be screen width zero, and ignored

First, let’s create a couple of helper functions.

Breakpoints Difference Function

To calculate the slope, we’ll want to know the difference between the largest and smallest breakpoint screen widths (ignoring the trivial size 0).

// assumes grid-breakpoints are in order, and ignores the first one
@function get-breakpoints-difference() {
  $idx: 0;
  $lowest: 0;
  $highest: 0;
  @each $key, $breakpoint in $grid-breakpoints {
    @if ($idx == 1) {
      $lowest: $breakpoint;
    }

    $highest: $breakpoint;
    $idx: $idx + 1;
  }

  @return $highest - $lowest;
}

Get 2nd Breakpoint (the lowest non-zero one)

Next, here’s a quick little function to get the lowest non-zero breakpoint’s screen width.

// get the screen width defined by the 2nd smallest breakpoint
@function get-2nd-breakpoint() {
  $idx: 0;

  @each $key, $breakpoint in $grid-breakpoints {
    @if ($idx == 1) {
      @return $breakpoint;
    }

    $idx: $idx + 1;
  }

}

The Linear Interpolation Mixin

Finally, we can use those to write a cleaner linear interpolation mixin.

@mixin interpolate-style( $style, $smallest, $largest ) {

  $slope: ( $largest - $smallest ) /  get-breakpoints-difference();
  $intercept: $smallest - ( $slope * get-2nd-breakpoint() );
  $idx: -1;

  #{$style}: $smallest;

  @each $key, $breakpoint in $grid-breakpoints {

    // skip the first two keys
    @if $idx > 0 {
      @include media-breakpoint-up($key) {
        #{$style}: $breakpoint * $slope + $intercept;
      }
    }

    $idx: $idx + 1;

  }

}

 

Example: Interpolating font-size

Here’s what it looks like in practice.

Say, I have a page title font defined as follows.

  • Desktop (Largest screen width) — font-size: 120px
  • Mobile (Smallest screen width) — font-size: 48px

We add our mixin as…

@include interpolate-style(font-size, 48px, 120px);

This is applied to grid breakpoints defined as follows….

$grid-gutter-width: 50px;
$grid-breakpoints: (
        xs: 0,
        sm: 375px + $grid-gutter-width,
        md: 720px + $grid-gutter-width,
        lg: 1075px + $grid-gutter-width,
        xl: 1800px + $grid-gutter-width
) !default;

When we compile the SASS, this produces the following CSS….

.c-heading-1 {
  font-size: 48px;
}
@media (min-width: 770px) {
  .c-heading-1 {
    font-size: 65.43158px; 
  } 
}
@media (min-width: 1125px) {
  .c-heading-1 {
    font-size: 83.36842px; 
  } 
}
@media (min-width: 1850px) {
  .c-heading-1 {
    font-size: 120px; 
  } 
}

 

 

I hope you find some value in this!

I know this explanation can use a little more love and diagrams to make it clearer. Let me know if you want me to go back and improve the explanation.  I’ll be happy to do it, if there’s interest.  🙂

Also, if you see a mistake or if there’s a better way to do this, feel free to share it!