Media Query and Retina Devices - Chapter 11

  • 0
I have kept the best for the last! And I must admit, the confusing one among the lot. Especially the part where we will be using media query along with mixins for conditional calls. To start with, SASS provides the ability to write nested media queries inside classes/ids.

/*Input scss nested media query*/

.header{
    min-height:50px;    
    
    @media only screen and (min-width:320px){
      background-color: red;
    }
    
    @media only screen and (min-width:768px){
      background-color: blue;
    }
    
    @media only screen and (min-width:1024px){
      background-color: green;
    }
}

/*And the same is outputted in css as:  */

.header {
  min-height: 50px;
}

@media only screen and (min-width: 320px) {
  .header {
    background-color: red;
  }
}

@media only screen and (min-width: 768px) {
  .header {
    background-color: blue;
  }
}

@media only screen and (min-width: 1024px) {
  .header {
    background-color: green;
  }
}

Here, we can see that, we were able to write the media query for required breakpoints inside the same element / class itself. So as and when we write, we exactly  know , where to make a change in case we need to tweak the style.  The sass compiler does the task of unwinding the queries and making them separate queries which comes one below the other in the css stylesheet.

C0oo000L  right !..

Hold on...more cool stuffs coming on..

Now, there are chances that there might be 20-30 partials, and each partial containing a ton of media query when we are planning to develop using this style. What if, we need to tweak the breakpoint slightly, say from 768px to 769px? We need to go to individual partials and change all the 768s to 769s, or based on the editor you are in , do a Replace all. Still, its messy.

We can set the breakpoint as a variable.
/* Variables.scss */
$mobile:320px;
$tablet: 768px;
$desktop: 1024px;

.header{
   min-height:50px;   
    
   @media only screen and (min-width:$mobile){
      background-color: red;
   }    
   @media only screen and (min-width:$tablet){
      background-color: blue;
   }    
   @media only screen and (min-width:$desktop){
      background-color: green;
   }
}

Now, if we need to change the breakpoint at a later point of time, we dont need to touch the other partials at all. The only place we need to change is the variable only and the new value gets reflected in all partials. Even better than previous ones right!

Lets explore more.

Some people like making everything short. If we look again, writing the same lengthy declaration of "@media only screen and (min-width: $mobile/$tablet/$desktop) " is boring right. Lets make that a variable too. And along with that, there is something called a "variable interpolation" technique with which we will put a placeholder for the breakpoint using " #{} " .
/* Variables.scss */

$mobile:320px;
$tablet: 768px;
$desktop: 1024px;

$mobile-mq: "only screen and (min-width: #{$mobile})";
$tablet-mq: "only screen and (min-width: #{$tablet})";
$desktop-mq: "only screen and (min-width: #{$desktop})";

.header{
    min-height:50px; 
    
    @media #{$mobile-mq}{
      background-color: red;
    }   
    @media #{$tablet-mq}{
      background-color: blue;
    }   
    @media #{$desktop-mq}{
      background-color: green;
    }
}
Now, the whole thing looks a bit more cleaner and sleek? And more importantly, maintainable ?

The breakpoint value, as well as the entire media query is now fully controlled from inside the variables.scss. So this becomes the only place where we need to make the query/breakpoint chnages for it to be reflected across the application.  Next time, if we need to make all the max-widths to max-device-widths, or min-width to min-device-width, we can do it with ease without polluting all the partials. A one line change is all that is needed ( along with a variable change if the breakpoint changes )

Now lets go for more! Lets introduce mixins along with media queries. This time, we will be writing a mixin which holds all 3 media queries and respond to each based on the viewport width! We will be introducing conditional logics into our scss stylesheet which was not possible on our normal css.

/* Variables.scss */

$mobile:320px;
$tablet: 768px;
$desktop: 1024px;

$mobile-mq: "only screen and (min-width: #{$mobile})";
$tablet-mq: "only screen and (min-width: #{$tablet})";
$desktop-mq: "only screen and (min-width: #{$desktop})";

/* Mixin that holds the queries */

@mixin change-header-color($device){
   @if $device == mobile{
       @media #{$mobile-mq}{
          background-color: red;
       }  
   }
    @else if $device == tablet{
        @media #{$tablet-mq}{
          background-color: blue;
       }  
   }
    @else if $device == desktop{
        @media #{$desktop-mq}{
          background-color: green;
       }  
   }   
}

/* Applying the mixin with the custom parameter passed */

.header{
    min-height:50px;
    
    @include change-header-color(mobile){
        background-color: red;
    }
    @include change-header-color(tablet){
        background-color: blue;
    }
    @include change-header-color(desktop){
        background-color: green;
    }    
}

The parameter passed ( Here, the string "mobile", "desktop" , "tablet" etc ) can be anything of your choice. Its just that it should match the "@if , @else if " clause in the mixin specified.

Thats quite a handful i guess! But yes, sass is powerful..and like Spiderman says, "With power, comes responsibilities.."!  So use it wisely.

Retina Devices: 
With apple being the pioneer of this whole "retina" revolution ( and along with that, stabbed a dagger into the ordinary developers chest :) .. lol.. already the poor webbie is smoking his head trying to figure out the right breakpoints, queries etc, and on top, another stuff that cannot be ignored.)  There are so much devices ( reference here at Viewport widths ) that needs to be considered, and when we commit on saying that "Our app or website is responsive, it pretty much mean that it should work in all these devices" ...( listed in the site is just a few in number..the tip of the iceberg...), it should work as we have promised.

The layout is something that will work thanks to the meta tag and its width=device-width that sets the width of the viewport to whichever device's viewport width or height. ( does it sounds confusing already? )

The simplest explanation is something like this. Consider a matrix of 1024 jugs. Each jug can contain a glass of water. To fill that we need 1024 glasses of water.  Now what if we increase the matrix to 2048 jugs? Can we fill the 2048 jugs with 1024 glasses of water? If we try to , then we need to pour half a glass on each jug instead of the full glass right? Thats more or less the retina stuff Vs the normal pixel stuff. [ Without peeping into a css pixel or a device pixel or its complications! ]. The problem is highlighted especially w.r.t images. When we say an image is 100px X 100px, its like saying 1024 jugs on a normal scenario, and if we take the same 100px X 100px in a retina scenario, ie the 2048 jugs, the image looks blurry. Why? Thats coz its half filled! Just like the half filled jugs which we mentioned previously.

To view the same image with same clarity, now we need a 200px X 200px image instead of a 100px X 100px.

We target the retina devices using device pixel ratio. To consider a device as retina, the value should be at-least greater than 1.5 whereas the normal device's value is 1.

In SASS, we can write a mixin to target retina devices. We can write a small example where, we have 2 images,  eg: facebook.png ( 30px X 30px ) and another image facebook-2x.png (60px X 60px ). The normal convention is to name the 2X image with an underscore or a hiphen along with the same name as that of the normal one.

We are going to write a parameterized mixin and along with the help of the ampersand operator, we will provide 1X image to normal devices and 2X images to those which is termed as retina.
@mixin retinize($file, $type, $width, $height) {
    background-image: url('../img/' + $file + '.' + $type);
    
    @media  (-webkit-min-device-pixel-ratio: 1.5),
            (min--moz-device-pixel-ratio: 1.5),
            (-o-min-device-pixel-ratio: 3/2),
            (min-device-pixel-ratio: 1.5),
            (min-resolution: 1.5dppx) {
    & {
          background-image: url('../img/' + $file + '-2x.' + $type);
          -webkit-background-size: $width $height;
          -moz-background-size: $width $height;
          background-size: $width $height;
      }
   }
}
Here, we have a mixin that accepts 4 parameters.
  1. The name of the file. ( whatever the name is )
  2. The  type of the file ( is it a png or jpg )
  3. Its dimensions ( width and height )
And the mixin says, on a normal flow, let the background image be that of a 1x image , and if that device is a retina, then overwrite the background image to that of a 2x image using &.
@mixin retinize($file, $type, $width, $height) {
    background-image: url('../img/' + $file + '.' + $type);
    
    @media  (-webkit-min-device-pixel-ratio: 1.5),
            (min--moz-device-pixel-ratio: 1.5),
            (-o-min-device-pixel-ratio: 3/2),
            (min-device-pixel-ratio: 1.5),
            (min-resolution: 1.5dppx) {
    & {
          background-image: url('../img/' + $file + '-2x.' + $type);
          -webkit-background-size: $width $height;
          -moz-background-size: $width $height;
          background-size: $width $height;
      }
   }
}

/* Applying the mixin with the custom parameter passed */

.header{
    min-height:50px;
    
    .logo{
         width:30px;
         height:30px;
         color:#fff;
         display:inline-block;
         @include retinize('facebook', 'png', 30px, 30px);
    } 
}

Here we said, we are calling the retinize mixin with the name of the image ( facebook ), the type ( png ) and its width and height ( 30px, 30px ).

Lets take a look at the output:

Here, on the left side, we can see on a normal desktop that it takes the 1x image (the black one ), and if we take a device that supports retina resolution, we can see that the 1x image is overridden (with a blue one ) . The background image url is changed in the Apple iPad browser simulator as shown in the right.





No comments:

Post a Comment