Last Updated: January 28, 2021
·
11.82K
· poteto

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.

3 Responses
Add your response

Nice writeup, and of course everyone should use SASS. ;-)

over 1 year ago ·

@dkewal I'm using the older Sass syntax that uses indentation instead of curly braces. Just a bit quicker to type.

over 1 year ago ·

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?

over 1 year ago ·