Media Queries in SCSS - Another Convenient Way to Use @media Screen

Hello!



Recently I encountered a problem with media queries in a project built on Angular. I don’t remember exactly what restrictions made me write a mixin to simplify my work, but I really want to share the result and get feedback. I met many other similar examples, but for work it is clearer and more convenient for me to use this particular one.



In general, for convenience, I made the following requirements for myself:



  1. Screen sizes must be rendered separately so that you can globally change the behavior in one place (for example, instead of "320px", simply transmit "xs").
  2. This mixin with media queries can be in more than one direction (for example, not always only max-width).
  3. Mixin can be used separately, overriding the described classes inside, or described in the body of the parent, overriding its properties.


So let's define any permissions we need. For instance:



$sizes: ("xs":320px, "sm":576px, "md":768px, "lg":992px, "xl":1200px);


First, let's write a mixin that would accept the desired range prefix and resolution, under which we are limited:



@mixin media($minmax, $media) {
  @each $size, $resolution in $sizes {
    @if $media == $size {
      @media only screen and (#{$minmax}-width: $resolution) {
        @content;
      }
    }
  }
}


In short, we pass the name of the screen resolution we need, looking for its value among those previously declared in the $ sizes variable. After we have found it, we substitute it together with the passed min or max (variable $ minmax).



An example of use, including in another mixin:



@mixin blocks-width {
  width: 400px;
  @include media("max", "md") {
    width: 100%;
  }
}


From this simple example we will get a mixin that changes the block width at <768px from 400px to 100%. The following examples should give the same result.



An example of use inside a class:



.blocks-width {
width: 400px;
  @include media("max", "md") {
    width: 100%;
  }
}


An example of use as a stand-alone media query:



.blocks-width {
width: 400px;
}

@include media("max", "md") {
.blocks-width {
    width: 100%;
}
}


But what if you need a media query, including one with a clear range (specify one resolution within which the media query will work)? Let's expand our mixins a bit.



Personally, it is convenient for me to describe the resolution as follows - if we need the md range, then we take the screen sizes between sm and md. If we want to rewrite the mixin so that we only pass one resolution, then we will have to find the previous value from the list. Since I did not find any method to do this quickly, I had to write a function:



@function getPreviousSize($currentSize) {
  $keys: map-keys($sizes);
  $index: index($keys, $currentSize)-1;
  $value: map-values($sizes);
  @return nth($value, $index);
}


To check how it works, use debug:



@debug getPreviousSize('md');


Next, our slightly redone code:



@mixin media($minmax, $media) {
  @each $size, $resolution in $sizes {
    @if $media == $size {
      @if ($minmax != "within") {
        @media only screen and (#{$minmax}-width: $resolution) {
          @content;
        }
      } @else {
        @if (index(map-keys($sizes), $media) > 1) {
          @media only screen and (min-width: getPreviousSize($media)+1) and (max-width: $resolution) {
            @content;
          }
        } @else {
          @media only screen and (max-width: $resolution) {
            @content;
          }
        }
      }
    }
  }
}


The logic is the same as for the previous functionality. But, if you want to apply a media query only in the range, for example, md, then write the following when calling the mixin:



@include media("within", "md") {
  .blocks-width {
    width: 100%;
  }
}


After that we will see the following compiled css:



@media only screen and (max-width: 768px) and (min-width: 577px)
.blocks-width {
    width: 100%;
}


Moreover, if we indicate the smallest screen size (we have it xs), for example:



@include media("within", "xs") {
  .blocks-width {
    width: 100%;
  }
}


Then we get a range from 0 to the corresponding smallest resolution:



@media only screen and (max-width: 320px)
.blocks-widthh {
    width: 100%;
}


Naturally, you can rewrite these media queries in the other direction, but personally I am used to layout from larger to smaller.



Thank you for attention!

UPD: the mixin has been slightly corrected so that the resolutions in limited ranges do not overlap :) Thanks for your attentionE_STRICT



All Articles