Search code examples
javascriptcsssassinterpolationlinear-interpolation

How can I automatically interpolate n intermediate values for a CSS property given an upper and lower bound?


If I have some media queries that set a value based on the current screen width how can I automate the computation of n intermediate steps?

As example: if I had a top value of 50% at 1080p and a value of 32% at 2160p I should be able to compute 4 intermediate steps:

@media(min-height: 1080px) {
  top: 50%;
}
@media(min-height: 1350px) {
  top: 45.5%;
}
@media(min-height: 1620px) {
  top: 41%;
}
@media(min-height: 1890px) {
  top: 36.5%;
}
@media(min-height: 2160px) {
  top: 32%;
}

I'm interested in any sort of possible solution: pure CSS, Sass, JS, etc.

(Also, could this kind of computation be possible for non linear values?)


Solution

  • You could use a mixin to generate the steps, it would be a basic @for loop.
    Depending on the flexibility you want for the mixin you'll need few arguments:

    • $stepsNb
    • $minWidthQuery
    • $maxWidthQuery
    • $property
    • $minValue
    • $maxValue

    Then the mixin will calculate the steps for you:

    @mixin interpolateSteps($stepsNb: 5, $minWidthQuery: 1080px, $maxWidthQuery: 2160px, $property, $minValue, $maxValue) {
        $step: ($maxWidthQuery - $minWidthQuery) / ($stepsNb - 1);
        $stepValue: ($maxValue - $minValue) / ($stepsNb - 1);
        
        @for $i from 0 to $stepsNb {
            @media(min-height: $minWidthQuery + ($step * $i)) {
                #{$property}: $minValue + ($stepValue * $i);
            }
        }    
    }
    

    You would use it like this:

    .foobar {
        @include interpolateSteps($property: top, $minValue: 50%, $maxValue: 32%);
    }
    

    And it would output:

    @media (min-height: 1080px) {
      .foobar {
        top: 50%;
      }
    }
    @media (min-height: 1350px) {
      .foobar {
        top: 45.5%;
      }
    }
    @media (min-height: 1620px) {
      .foobar {
        top: 41%;
      }
    }
    @media (min-height: 1890px) {
      .foobar {
        top: 36.5%;
      }
    }
    @media (min-height: 2160px) {
      .foobar {
        top: 32%;
      }
    }