Responsive Design: Media Queries And How To Make Them More Friendly
Responsive Design: Media Queries And How To Make Them More FriendlySep 21st 2015
In building responsive websites, whether Drupal or otherwise, we all need to harness the power of media queries.
Here is a brief overview of media queries, breakpoints and the basics of the ‘Mobile First’ approach and also some simple tricks for making media queries more friendly, semantic and easier to remember and use with LESS and SASS variables.
Media queries - The basics
A CSS Media Query (originally ‘media rule’ in CSS2, renamed ‘media query’ in CSS3) is a CSS statement which targets a specific media type and/or size/size range. Inside a media query statement we can nest a collection of CSS declarations which will style content only when the conditions of that parent media query statement are true.
So, if a media query targets a media type of ‘screen’ with a min-width of 320px, it’s nested CSS declarations will only have effect on screens (smartphones/tablets/computers etc) but only if the device has a screen with a display larger than 320px wide.
Breakpoints
In responsive design, a ‘breakpoint’ is a declared pixel width at which the layout of a web page or entire site should adjust (respond) to better display its content on the screen size of the device it is being viewed on. Commonly designers and developers set breakpoints at 2 or 3 size ranges to separately target mobile, tablet and larger desktop/laptop computers.
There’s no actual limit (other than common sense) to the amount of breakpoints you could add, but these 3 are a pretty good rule of thumb and cover most needs.
The beauty of the system is that it encourages us to target screen size ranges (all screens between ‘these sizes’) and not specific device types, so when a new model of smartphone comes out (for instance), we can be pretty sure the site will appear correctly as the device’s screen will be of a size that falls within an existing range that we are already targeting.
Mobile First? (min vs max)
First thing to decide is in which direction you want to media queries to work.
By this I mean whether you want to set max-width or min-width breakpoints.
You can of course use a mixture of both, but here we’re all about making things more straightforward, so I would avoid ‘crossing the streams’ and stick to one strategy.
Max Width breakpoints:
Everything from zero or the last, narrower breakpoint, up to ‘this width’ .
Used mostly in ‘Desktop First’ and ‘Retro Fitted Mobile’ builds.
Min Width breakpoints:
Everything above ‘this width’ to infinity, or until overridden by another, wider breakpoint.
Used mostly in modern ‘Mobile First’ builds.
‘Mobile First’ is widely accepted as the best way to approach responsive design, so I would advise the use of Min Width breakpoints.
You can find much more in-depth information about Mobile First from the person regarded as its original architect Luke Wroblewski.
In Mobile First theory we start without any breakpoints or media queries (in essence targeting all screen sizes of 1px wide to infinity) and here set all default, site-wide styles as well as any mobile specific styles for (so that we know all screens no matter how small will be covered) and then we create media query breakpoints at which we override the default styles for layout or elements which need to appear differently on larger screens. (As opposed to styling the whole site for desktop and then overriding for smaller screens.)
This can take a little getting used to and feel a bit ‘backwards’ for developers used to building desktop sites, who may still consider mobile a ‘bolt on’, secondary consideration, but it’s surprising how easy the switch in thinking can be.
Example of a Mobile First media query
body{
background: white;
}
@media screen and (min-width:768px) {
body{
background: black;
}
}
In this example, the body of the document will have a white background on all screens smaller than 768px - This is it’s default ‘Mobile First’ setting.
Then on all screens larger than 768px the body will have a black background.
Which and how many breakpoints to choose?
You could in theory, create a mobile first website without any media queries or breakpoints.
You could serve the same mobile layout to all screen sizes. But because in most cases mobile layout consists of 100% width single column elements, it would start to become very difficult to use and read on larger screens. So the bare minimum we would usually consider using is one breakpoint to distinguish between mobile and more traditional desktop layouts.
The size of 768px used in the example above would work well for this, as our site would show mobile layout on mobiles and many tablets in portrait orientation, then on tablets in landscape orientation, laptops and desktops, another layout could be displayed.
I use Bootstrap 3 as my main front-end framework and ‘out of the box’ Bootstrap 3 has 3 defined breakpoints:
sm - 768px and up to...md - 992px and up to...lg - 1200px and up to infinity
This covers our Mobile First defaults up to 767px, a tablet aimed size range of 768px-991px, a traditional desktop width of 992px-1199px and a more modern, wider desktop width of 1200px and above.
*Personally, I usually add a smaller, custom breakpoint of around 480px, because I find in practice the range of 0-767px, before the first ‘sm’ breakpoint kicks in can sometimes be a bit wide for every part of my layout. Interestingly, one of the promised updates in the imminent release of Bootstrap 4 is that it will come with a similar ‘extra small’ breakpoint as standard.
Media queries can become surprisingly complicated to keep track of. This is where LESS and SASS variables can be a godsend.
LESS and SASS Variables - Making media queries more user friendly
Although not the most complicated coding syntax in the world, media queries can be hard to keep track of, once you’ve got 3 or 4 different sizes to contend with. Having to remember the precise pixel width you are targeting and in which direction can become surprisingly complicated, especially when we start using inline queries (more coming on that later.) This is where LESS and SASS variables can be a godsend.
Here’s a LESS example:
In our variables.less (or similar) create a variable called ‘@tablet’. *Notice we use a tilde (~) in front of our value string, so that the quote marks are not included when processed into CSS. - We need to use quotes around our variable value as LESS syntax requires this if a value contains a string of different tokens, but once compiled to CSS, these are not needed.
@tablet: ~“screen and (min-width: 768px)”;
Now instead of using
@media screen and (min-width:768px) {
body{
background: black;
}
}
We can use the variable to target 768px and above.
@media @tablet {
body{
background: black;
}
}
In SASS, the same principle applies, but our syntax is slightly different. Our variable is denoted by ‘$’ as opposed to ‘@’ and instead of adding a tilde to interpolate our value string and remove the quotes at the point at which our variable is declared, we use #{} around our variable name at the point at which it’s referenced in our code to do the same thing.:
$tablet: “only screen and (min-width: 768px)”;
We can then use:
@media #{$tablet} {
body{
background: black;
}
}
‘Nested media queries’
So that’s given us a method of creating more memorably named variables to use in place or full hand media queries in our code, but it can still feel a bit disjointed and counter intuitive to set initial global declarations in one point of a file and then keep jumping to another point in a file or to another file altogether to set our media query overrides. Also imagine if you only want to override an attribute value which was a few levels down within a LESS or SASS nest. For our media query override to work we would have to include the entire nest up to the point at which the value we want to change appears.
Say we want to change the color of all links on screens above 768px wide, but only if they are inside paragraphs, which are inside a div with the class of ‘thisdiv’, which are in turn inside a div with an ID of ‘container’.
The original global code might look like this:
#container{
.thisdiv{
p{
a{
color: red;
}
}
}
}
Our media query override would need to include this whole list (LESS version shown):
@media @tablet{
#container{
.thisdiv{
p{
a{
color: blue;
}
}
}
}
}
That’s a heck of a lot of nesting to keep on top of and if we add a new nest level in our original declaration between say '#container' and '.thisdiv', we would also have to update the nesting in our media query.
Luckily current versions of both LESS and SASS allow for the nesting of media queries so that they can essentially be used inline within existing declarations.
So with the above example instead of having to re-write the entire nest in a separated part of our file or in a separate file, we can just specify the exact element we want to override from within the original global declaration.
So instead of having to write:
#container{
.thisdiv{
p{
a{
color: red;
}
}
}
}
@media @tablet{
#container{
.thisdiv{
p{
a{
color: blue;
}
}
}
}
}
We can just write:
#container{
.thisdiv{
p{
a{
color: red;
@media @tablet{
color:blue;
}
}
}
}
}
*Notice the media query is nested inside the ‘a’ attribute itself, after its initial, global value.
Bootstrap 3 specific queries
In Bootstrap 3 we can go a little further to make sure we fully integrate our media query variables into the framework. Bootstrap depends on a grid system which is based around a collection of pre existing breakpoints.
Although most users leave these existing breakpoints where they are, if you do need or want to changes these to custom values of your own choice, this can be done by identifying and changing the values of the corresponding variables in variables.less. For this reason it makes sense that our media queries use these existing variables as their values.
Why? Because if we (or anyone else) updates the value of the grid breakpoint widths, our media query variables will then in turn automatically receive these new values.
Here is our friendly name Bootstrap LESS variables:
@mobile-lg: ~"screen and (min-width: @{screen-xs-min})";
@tablet: ~"screen and (min-width: @{screen-sm-min})";
@normal: ~"screen and (min-width: @{screen-md-min})";
@wide: ~"screen and (min-width: @{screen-lg-min})";
I usually place these at the top of variables.less for easy reference.
So to re-iterate; instead of explicitly stating ‘768px’ as the value for min-width in the @tablet variable, we instead reference the existing Bootstrap variable ‘@{screen-sm-min}’. This means if the integral Bootstrap grid variable is updated, our media query variable and all dependant references will use this new value automatically.
Other Front End Frameworks Are Available!
There are of course other front-end frameworks available other than Bootstrap such as Foundation by Zurb as well as some Drupal specific responsive grid-based systems like AdaptiveTheme and Omega. I only cover Bootstrap here as an example and also because it is currently the most popular framework around also but not least because it's what I personally use most and know most about. All of the principles up to the Bootstrap specific queries should still be just as relevant, no matter which framework you use or whether you indeed use one at all.
Further reading
The above is intended as quite a top level, overview of media queries and their application. For more in-depth information on the technologies and concepts used I’d recommend the following further reading:
W3C Schools: Media QueriesLuke Wroblewski - Architect Of Mobile FirstLESSSASSBootstrap 3
Written by: Martin White, Drupal Themer
Microserve is a Drupal Agency based in Bristol, UK. We specialise in Drupal Development, Drupal Site Audits and Health Checks, and Drupal Support and Maintenance. Contact us for for further information.