Awesome @media queries with Sass and @content
Sass has a very useful feature that is generally not widely known: @content. You can almost think of @content
like yield
- it lets us define a Sass mixin which can then have nested CSS.
This makes @media
queries a joy to work with. No more fiddling around with min, max & device widths and trying to visualize what you're trying to achieve in your head. The following module will likely save you precious hours of your time as it reduces code repetition (DRY) and makes use of semantic naming (instantly understandable).
tl;dr View source on GitHub
How it works
Define variables
First, we need to setup the standard device widths we want to target. For simplicity's sake, I'll use only the iPhone, iPad and Desktop sizes adapted from CSSTricks in this example.
$iphone-portrait: 320px
$iphone-landscape: 480px
$ipad-portrait: 768px
$ipad-landscape: 1024px
$desktop: 1224px
$desktop-large: 1824px
Now on to the good stuff.
Specific @media queries
We'll make use of Sass mixins and its ability to pass in arguments to target very specific devices. It's a good idea to come up with something semantic (has meaning) so it's instantly clear what you mean when you or someone else reads it a few weeks later.
Let's go with respond-to
, so I can write something like this later on:
.foobar
+respond-to(ipad-portrait)
width: 50%
+respond-to(iphone-portrait)
padding: 2.5%
That is amazingly readable, and instantly makes more sense than deciphering something like @media only screen and (max-width: 320px) and (orientation: portrait)
.
Here's an excerpt of our Sass function for respond-to
:
=respond-to($device)
@if $device == retina-display
@media only screen and (-webkit-min-device-pixel-ratio: 2)
@content
@if $device == iphone
@media only screen and (min-width: $iphone-portrait) and (max-width: $iphone-landscape)
@content
@if $device == ipad
@media only screen and (min-width: $ipad-portrait) and (max-width: $ipad-landscape)
@content
...etc
Now this is pretty long but it's basically just a simple case function. The magic lies in @content
, which lets us pass in the CSS we want to apply under the @media
query above it. I'm sure you can also think up various other ways to make use of @content
.
General @media queries
Now say we don't want to get so specific, and want to include some CSS for anything smaller than an iPad - landscape. Instead of doing something silly like using multiple variations of the above respond-to
function, let's just define a new one which will apply-to
many devices.
Here's the excerpt of our function:
=apply_to($ltgt, $device)
$extrema: null
@if $ltgt == less-than
$extrema: max
@if $ltgt == greater-than
$extrema: min
@if $device == iphone-portrait
@media only screen and (#{$extrema}-width: $iphone-portrait)
@content
@if $device == ipad-landscape
@media only screen and (#{$extrema}-width: $ipad-landscape)
@content
...etc
Here we just added on a simple if
method that lets us write more meaningful mixin names and arguments. Why $extrema
?
Now we can do this:
.foobar
+apply-to(less-than, ipad-landscape)
content: 'that is awesome'
background: black
Sass is amazing and you should be using it.
Written by Lauren
Related protips
3 Responses
Nice writeup, and of course everyone should use SASS. ;-)
@dkewal I'm using the older Sass syntax that uses indentation instead of curly braces. Just a bit quicker to type.
So you can define within an element that certain styles can respond to certain devices? Such as your example using the respond-to method you can give one element 3 different variations of padding, for 3 different devices, without having to define all your @media queries someone else.
If what you say is true I might just go punch a goat in excitement.
and will this work on Sass with Rails?