Ad
`, } ); } })();

Using media queries in CSS as part of responsive websites is bread and butter stuff to todays front-end developer. Using preprocessors to make them more comfortable to write and easier to maintain has become common practice as well.

I spent a few months experimenting with a dozen different approaches to media queries in Sass and actually used a few in production. All of them eventually failed to cater for everything I needed to do in an elegant way. So I took what I liked about each of them and created a solution that covered all scenarios I came across.

Why use a preprocessor at all?

That’s a fair question. After all, what’s the point of doing all this if one can simply write media queries using pure CSS? Tidiness and maintainability.

The most common use for media queries is the transformation of a layout based on the browser’s viewport width. You can make a layout adapt in such a way that multiple devices with different screen sizes can enjoy an optimal experience. As a consequence, the expressions used to define the media queries will make reference to the typical screen width of those devices.

So if your code contains 5 media queries that target tablet devices with a width of 768px, you will hardcode that number 5 times, which is something ugly that my OCD would never forgive. First of all, I want my code to be easy to read to the point that anyone understands instantly that a media query is targeting tablet devices just by looking at it – I reckon the word tablet would do that better than 768px.

Also, what if that reference width changes in the future? I hate the idea of replacing it in 5 instances around the code, especially when it’s scattered around multiple files.

A first step would be to store that breakpoint in a variable and use it to construct the media query.

/* Using plain CSS */
@media (min-width: 768px) {
  
}

/* Using SCSS variables to store breakpoints */
$breakpoint-tablet: 768px;
@media (min-width: $breakpoint-tablet) {
  
}

Another reason to write media queries with a preprocessor like Sass is that it can sometimes provide some precious help with the syntax, in particular when writing an expression with a logical or (represented with a comma in CSS).

For example, if you want to target retina devices, the pure CSS syntax starts getting a bit verbose:

/* Plain CSS */
@media (min-width: 768px) and 
       (-webkit-min-device-pixel-ratio: 2), 
       (min-width: 768px) and 
       (min-resolution: 192dpi) { 
}

/* Using variables? */
@media (min-width: $bp-tablet) and ($retina) { // or #{$retina}

}

It does look nicer, but unfortunately it won’t work as expected.

A problem with logic

Because of the way the CSS “or” operator works, I wouldn’t be able to mix the retina conditions with other expressions since a (b or c) would be compiled into (a or b) c and not a b or a c.

$retina: "(-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi)";
 
// This will generate unwanted results!
@media (min-width: 480px) and #{$retina} {
  body {
    background-color: red;
  }
}
/* Not the logic we're looking for */
@media (min-width: 480px) and (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
  body {
    background-color: red;
  }
}

I realized I needed something more powerful, like a mixin or a function, to address this. I tried a few solutions.

Dmitry Sheiko’s technique

One I tried was Dmitry Sheiko’s technique, which had a nice syntax and includes Chris’ retina declaration.

// Predefined Break-points
$mediaMaxWidth: 1260px;
$mediaBp1Width: 960px;
$mediaMinWidth: 480px;

@function translate-media-condition($c) {
  $condMap: (
    "screen": "only screen",
    "print": "only print",
    "retina": "(-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: 120dpi)",
    ">maxWidth": "(min-width: #{$mediaMaxWidth + 1})",
    "<maxWidth": "(max-width: #{$mediaMaxWidth})", 		
    ">bp1Width": "(min-width: #{$mediaBp1Width + 1})",
    "<bp1Width": "(max-width: #{$mediaBp1Width})",
    ">minWidth": "(min-width: #{$mediaMinWidth + 1})",
    "<minWidth": "(max-width: #{$mediaMinWidth})"
  );
  @return map-get( $condMap, $c );
}

// The mdia mixin
@mixin media($args...) {
  $query: "";
  @each $arg in $args {
    $op: "";
    @if ( $query != "" ) {
      $op: " and ";
    }
    $query: $query + $op + translate-media-condition($arg);
  }
  @media #{$query}  { @content; }
}

But the problem with logical disjunction was still there.

.section {
  @include media("retina", "<minWidth") {
    color: white;
  };
}
/* Not the logic we're looking for */
@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: 120dpi) and (max-width: 480px) {
  .section {
    background: blue;
    color: white;
  }
}

Landon Schropp’s technique

Landon Schropp’s was my next stop. Landon creates simple named mixins that do specific jobs. Like:

$tablet-width: 768px;
$desktop-width: 1024px;

@mixin tablet {
  @media (min-width: #{$tablet-width}) and (max-width: #{$desktop-width - 1px}) {
    @content;
  }
}

@mixin desktop {
  @media (min-width: #{$desktop-width}) {
    @content;
  }
}

He has a single-responsibility retina version as well.

But another problem hit me when I was styling an element that required additional rules on intermediate breakpoints. I didn’t want to pollute my list of global breakpoints with case-specific values just so I could still use the mixin, but I definitely didn’t want to forgo the mixin and go back to using plain CSS and hardcoding things every time I had to use custom values.

/* I didn't want to sometimes have this */
@include tablet {

}

/* And other times this */
@media (min-width: 768px) and (max-width: 950px) {

}

Breakpoint technique

Breakpoint-sass was next on my list, as it supports both variables and custom values in its syntax (and, as a bonus, it’s really clever with pixel ratio media queries).

I could write something like:

$breakpoint-tablet: 768px;

@include breakpoint(453px $breakpoint-tablet) {

}

@include breakpoint($breakpoint-tablet 850px) {

}

/* Compiles to: */
@media (min-width: 453px) and (max-width: 768px) {

}

@media (min-width: 768px) and (max-width: 850px) {

}

Things were looking better, but I personally think that Breakpoint-sass’ syntax feels less natural than Dmitry’s. You can give it a number and it assumes it’s a min-width value, or a number and a string and it assumes a property/value pair, to name just a few of the combinations it supports.

That’s fine and I’m sure it works great once you’re used to it, but I hadn’t given up on finding a syntax that was both simple and as close as possible to the way I orally describe what a media query must target.

Also, if you look at the example above you’ll see that a device with a width of exactly 768px will trigger both media queries, which may not be exactly what we want. So I added the ability to write inclusive and exclusive breakpoints to my list of requirements.

My (Eduardo Bouças’s) technique

This is my take on it.

Clean syntax, dynamic declaration

I’m a fan of Dmitry’s syntax, so my solution was inspired by it. However, I’d like some more flexibility in the way breakpoints are created. Instead of hardcoding the names of the breakpoints in the mixin, I used a multidimensional map to declare and label them.

$breakpoints: (phone: 640px, 
               tablet: 768px, 
               desktop: 1024px) !default;

@include media(">phone", "<tablet") {
}

@include media(">tablet", "<950px") {
}

The mixin comes with a set of default breakpoints, which you can override anywhere in the code by re-declaring the variable $breakpoints.

Inclusive and exclusive breakpoints

I wanted to have a finer control over the intervals in the expressions, so I included support for the less-than-or-equal-to and greater-than-or-equal-to operators. This way I can use the same breakpoint declaration in two mutually exclusive media queries.

@include media(">=phone", "<tablet") {

}

@include media(">=tablet", "<=950px") {

}

/* Compiles to */
@media (min-width: 640px) and (max-width: 767px) {

}

@media (min-width: 768px) and (max-width: 950px) {

}

Infer media types and handle logic disjunction

Similarly to the breakpoints, there’s a list for media types and other static expressions declared by default (which you can override by setting the variable $media-expressions). This adds support for optional media types, such as screen or handheld, but it’s also capable of correctly handling expressions with logical disjunctions, such as the retina media query we saw before. The disjunctions are declared as nested lists of strings.

$media-expressions: (screen: "screen", 
                    handheld: "handheld",
                    retina2x: 
                    ("(-webkit-min-device-pixel-ratio: 2)", 
                    "(min-resolution: 192dpi)")) !default;

@include media("screen", ">=tablet") {

}

@include media(">tablet", "<=desktop", "retina2x") {

}

/* Compiles to */
@media screen and (min-width: 768px) {

}

@media (min-width: 769px) and 
       (max-width: 1024px) and 
       (-webkit-min-device-pixel-ratio: 2),
       (min-width: 769px) and 
       (max-width: 1024px) and 
       (min-resolution: 192dpi) {

}

There’s no rocket science under the hood, but the full implementation of the mixin isn’t something I could show in just a few lines of code. Instead of boring you with huge code snippets and neverending comments, I included a Pen with everything working and I’ll briefly describe the process it goes through to construct the media queries.

How it works

  1. The mixin receives multiple arguments as strings and starts by going through each one to figure out if it represents a breakpoint, a custom width, or one of the static media expressions.
  2. If an operator is found, it is extracted and any matching breakpoint will be returned, or else we assume it’s a custom value and cast it to a number (using SassyCast).
  3. If it’s a static media expression, it checks for any or operators and generates all the combinations necessary to represent the disjunction.
  4. The process is repeated for all the arguments and the results will by glued together by the and connector to form the media query expression.

If you’d like to look at the complete Sass for it, it’s here. It’s called include-media on GitHub.

Final thoughts

Update!

Eduardo made a website for his approach: @include-media.

Comments

  1. Jonathan
    Permalink to comment#

    That is a monster of a mixin. Nicely done.

    It’s probably worth doing a post or two to teach folks how to go about thinking about SASS/SCSS mixins as thoroughly as this one has been thought out. By far my favorite part of this post was reading through that mixin.

    Great solution to media query syntax, too! All around, good stuff.

  2. wiziwiz
    Permalink to comment#

    How does this differ from bootstraps simple
    …/* Extra small devices (phones, less than 768px) /
    /
    No media query since this is the default in Bootstrap */

    /* Small devices (tablets, 768px and up) */
    @media (min-width: @screen-sm-min) { … }

    /* Medium devices (desktops, 992px and up) */
    @media (min-width: @screen-md-min) { … }

    /* Large devices (large desktops, 1200px and up) */
    @media (min-width: @screen-lg-min) { … }…

    • Joseph Rex
      Permalink to comment#

      Real front-end developers usually don’t want to rely on a framework like bootstrap all the time. It allows you load up unnecessary stylesheet. The whole point of this is creating a better media query from the stylesheet rather than having some ready classes from a bunch of rules in a framework

    • Robert Smith
      Permalink to comment#

      This is how I do it. You don’t have to go loading in a huge bloated framework like BS. There’s nothing wrong with taking the good parts and putting them in your own.
      I do the same also with the Grid. You can reduce bloat in the grid too by making use with @if and creating an options file where you can easily add or remove parts of the grid.

    • Tony
      Permalink to comment#

      @wiziwiz, because it’s not bloated with a million different things you are probably not going to use.

      In general when I use something like Bootstrap or Foundation it’s for a quick prototype or a click through ( to demo a user story or whatever).

      Then I build my own (mobile first) stylesheet. Susy or zen grids are great tools, that make frameworks like Bootstrap and Foundation not needed.

  3. rxrrctct
    Permalink to comment#

    Are em-based MQ now a thing of the past?

    • Tony
      Permalink to comment#

      px’s are a better way for media-queries and em’s are better for typography and block level/ inline elements. Just my 2 cents

    • Ricardo Zea
      Permalink to comment#

      I prefer em-based media queries because if the user decides to zoom-in their browser will pick up the media queries and see the responsive site, thus the website never breaks.

      Also, some devices report a higher font size than 16px and if that’s the case they will also see the responsive site because of the em-based MQs.

      http://bradfrost.com/blog/post/7-habits-of-highly-effective-media-queries/#relative

      http://blog.cloudfour.com/the-ems-have-it-proportional-media-queries-ftw/

      Some even suggest usin REMs:

      [Solved] EMs for media queries and layout

      But, https://twitter.com/brad_frost/status/335259353466691584

    • Chris Coyier
      Permalink to comment#

      The zoom issue with px-based media queries problem was a problem in Chrome that’s long been fixed. And since old version of Chrome don’t stick around, I wouldn’t worry about it much. It’s funny how ideas like that can stick in our heads.

    • Robert Smith
      Permalink to comment#

      I agree with Tony. EMs / REMs for typographical elements or wrappers or those you want related to the typography. Things that influence the structure should either get % or px. That is the philosophy that has served me well so far.

    • Mike Mai
      Permalink to comment#

      Em isn’t precise enough, I’ve ran into problems where there’s an 1px gap between breakpoints (if you are defining ranged bpts instead of overlapping bpts). Px is the smallest unit, it’s exact.

    • Tony
      Permalink to comment#

      @Ricardo it’s a conditional statement

    • Tony
      Permalink to comment#

      @mike mai This is exactly what I’ve experienced while setting breakpoints with ems, I had to fiddle and fuss to get the breakpoints to fire, the math just didn’t work with a base font-size of 16px or whatever you set it to, the formula doesn’t work as expected. ( sorry I’m not going to make an example, just try it yourselves)

    • Alastair
      Permalink to comment#

      @rxrrctct absolutely not. I’m not sure we should be using px for anything these days.

      @Tony I’d disagree with you re: px media queries. Using em based media queries can help make your site accessible. Highly recommend reading this blog post – it’ll probably change your mind entirely.

      http://blog.cloudfour.com/the-ems-have-it-proportional-media-queries-ftw/

    • Tony
      Permalink to comment#

      @Alistar, I love using ems, I use them and rems.
      I find it funny that the article you linked to the author states that

      ‘I like SASS. I use it for projects where I’m flying solo. But the >complexity of the breakpoints of this site—SASS is weak when it comes >to breakpoints, as you cannot use variables for them—made for some ugly >compiled code that was difficult to debug. Sounds like we Cloud Four >folks need to attend some of those great recent talks about CSS m>anagement and sanity, eh?’

      I just had a few unexpected results using em’s and breakpoints in my ScSS files

  4. Don Droga
    Permalink to comment#

    Not sure if I’m ready to abstract the elegance of CSS.

  5. Ottomen
    Permalink to comment#

    It’s kind of a strange way to maintain media queries. I bet that the ordinary CSS syntax is better in this case.

  6. Hugo Giraudel
    Permalink to comment#

    Massive work, obviously. One question though, why not taking advantage of the fact Sass combines media queries rather than moving all the logic and (de)structuration to SassScript?

    Using Landon Schropp’s technique, you could have something like this (although I don’t like having a mixin per breakpoint, it is definitely not scalable):

    .component {
      // Regular content
      @include tablet {
        // Tablet specific content
        @media (max-width: 950px) {
          // Tablet-but-not-too-wide content
        }
      }
    }
    

    This syntax not only clearly defines its intent to style the component for “tablet” breakpoint, but also adds an extra requirement at the same time.

    Over things you might want to try:
    – Sass-MQ by @Kaelig, used at The Guardian and The Financial Times (among others): https://github.com/sass-mq/sass-mq
    – Breakpoint-slicer by @lolmaus, which is syntactic sugar over Breakpoint: https://github.com/lolmaus/breakpoint-slicer

    For people interested with the topic, I have written an article about Responsive Breakpoints Management at SitePoint: http://www.sitepoint.com/managing-responsive-breakpoints-sass/. In case that helps.

    • Sara Soueidan
      Permalink to comment#

      That was my very first thought when I read the article.

      Obviously a tremendous work has been put into this—kudos to Eduardo! But I would never “pre-specify” breakpoints and style content based on them.

      The content should define the breakpoints, not the other way around.

    • Jacob Falk Christensen
      Permalink to comment#

      but it does allow you to let the content define the breakpoint like this:

      @include media('<920px') {
         // something
      }
      

      I actually think its very flexible and intuitive(to me anyway).

      I started using this mixin right away. Thank you so much for this work Eduardo.

    • Rob
      Permalink to comment#

      Sometimes you don’t have the luxury of knowing the content. Especially when designing web software that is configurable and custom per client. You would need to know the content and not really change it.

  7. Moriartie
    Permalink to comment#

    Personally i’m not a big fan of breakpoints named after device categories (tablet, phone,…) for obvious reasons. (e.g. a device with a screen width of 1024px can literally be everything from phone to desktop)

    I think the brad frost approach to name things is the least bad solution: http://bradfrost.com/blog/post/7-habits-of-highly-effective-media-queries/#content
    ($bp-small, $bp-small-2, $bp-med, $bp-med-2,…)

    • Eduardo Boucas
      Permalink to comment#

      I appreciate that, but the good thing about this solution is that it’s flexible enough to do whatever you want. You can redefine $breakpoints to use that naming convention instead of the device names it comes with by default.

  8. Jason
    Permalink to comment#

    I really think that the content should define the breakpoints and not some predetermined device width that will change. Devices will continue to vary in size so why not have the elements in the design respond individually as the width changes? You can still do this with a mixin very simply…

    @mixin respond-at($media) {
        @media only screen and (min-width: $media){ @content; }
    }
    

    use:

    .some-element{
      //...mobile first stuff
      @include respond-at(600px){
        //.... do something
      }
    }
    
    • Sino
      Permalink to comment#

      I must agree. As newer versions of phones and tablets come out (far too quickly nowadays), it seems that it would only be logical to implement breakpoints based on content. It seems to be that by using this method, less time can be spent on site maintenance.

    • mazurka
      Permalink to comment#

      This is a technique I have been using as well, blended with landon schropps technique. Working mobile first also simplifies this for me immensely. I have used a break-one, break-two, etc (similar to schropps approach) as a general guideline and then a break-any as you described (which is probably used most often along with break-one) and generally avoid any max-width media quries.

    • Ricardo Zea
      Permalink to comment#

      @Jason, dude, this mixin it’s awesome.

      Good bye naming media queries!

  9. Monika
    Permalink to comment#

    Re: px vs em-based media queries, while browser “zoom” in Chrome now treats both the same (eg. using ctrl/cmd +/-), if users choose instead to up their text-size inside browser settings, px-based media queries will not kick in. For that reason I still prefer em-based, although I’m not sure what the more common scenario is among users who zoom (browser or text-based)
    http://codepen.io/mpiotrowicz/pen/pvNNXM/

    • Tony
      Permalink to comment#

      Here you go: http://bit.ly/1xD75tZ

    • Ricardo Zea
      Permalink to comment#

      Confirmed! Monika’s demo works as described.

      Granted, it was easier to change only the text size in Firefox than in Chrome and see the results.

      Thanks for sharing.

    • Rob
      Permalink to comment#

      I generally prevent them tinkering in browser settings by doing a reset stylesheet. You have more control over presentation and are taking the default text size out of the equation.

  10. Mike Mai
    Permalink to comment#

    I do this. It’s got the benefits of using common breakpoints and using custom ones based on the content. Learned from Tim Knight:

    @mixin bp($min: 0, $max: 0) {
      $type: type-of($min);
    
      @if $type == string {
        @if $min == small { 
          @media only screen and (max-width: 480px) { @content; }
        }
    
        @else if $min == medium {
          @media only screen and (min-width: 481px) and (max-width: 1440px) { @content; }
        }
    
        @else if $min == large {
          @media only screen and (min-width: 1441px) { @content; }
        }
    
        @else if $min == mobile {
          @media only screen and (max-width: 768px) { @content; }
        }
    
        @else if $min == desktop {
          @media only screen and (min-width: 769px) { @content; }
        }
    
        // Otherwise pass a warning to the compiler as to the appropriate options
        @else {
          @warn "The breakpoint mixin supports the following attributes: mobile, desktop, small, medium, large";
        }
      }
      @else if $type == number {
        // Allow for custom parameters for min and max size
        $query: "all" !default;
        @if      $min != 0 and $max != 0 { $query: "(min-width: #{$min}) and (max-width: #{$max})"; } // set both min and max
        @else if $min != 0 and $max == 0 { $query: "(min-width: #{$min})"; } // set just min
        @else if $min == 0 and $max != 0 { $query: "(max-width: #{$max})"; } // set just max
        @media #{$query} { @content; }
      }
    }
    
  11. Ferdy
    Permalink to comment#

    Maybe I am the odd one out here, but I use a system as follow…

    In main.scss, I load general.scss which has styles required in all cases. Next, also in main.scss, but below the general import I have all breakpoints listed, where each criteria imports the “sub” SCSS file for that specific breakpoint.

    So general.scss has the generic (S)CSS, typically the mobile-first styles, which are then extended where needed in larger breakpoints. I love this clean separation. There’s no breakpoint code repeated anywhere and there is a clean distinction between base styles and extended styles. In fact, just from the size of those “sub” SCSS files you can see the quality of your design. The bigger they are, the less responsive in fact the design is.

    To each their own, but I’m curious why people use all of the above methods, to me they seem unneeded? The only disadvantage of my approach is needing to open multiple files if you want get a full grasp of a selector, but in practice this does not pose as an issue to me.

    • Ricardo Zea
      Permalink to comment#

      You mind sharing the files so we can see your structure?

    • Ferdy
      Permalink to comment#

      @Ricardo: Sure, here goes, this is the content of projectname.scss, which is my main SCSS file:

      @import 'compass';
      @import 'theme';
      @import 'mixins';
      @import 'init';
      @import 'main';
      @import 'map';
      @import 'content';
      @import 'modal';
      @import 'fullscreen';
      @import 'general';
      @import 'forms'; 
      @import 'nav';
      
      /* default styles apply to smartphones portrait (~320px width), rest is enriched here <em>/
      @media screen and (min-width: 0em) {  @import 'mode_xxs'; }   /</em> small phone <em>/
      @media screen and (min-width: 35em) {  @import 'mode_xs'; }   /</em> big phone (560/16) <em>/
      @media screen and (min-width: 40em) {  @import 'mode_s'; }    /</em> small tablet (640/16) <em>/
      @media screen and (min-width: 48em) {  @import 'mode_m'; }    /</em> small tablet (768/16) <em>/
      @media screen and (min-width: 62em) {  @import 'mode_l'; }    /</em> small desktop or tablet (1024/16) <em>/
      @media screen and (min-width: 62em) {  @import 'mode_l'; }    /</em> small desktop or tablet (1024/16) <em>/
      @media screen and (min-width: 116em) {  @import 'mode_xl'; }  /</em> large desktop (1900/16) */
      

      As you can see, I first load base styles, which are divided in separate files per feature. All of those base styles combined essentially is the mobile-first state.

      Next, the breakpoints come, where based on the viewport, progressively extra SCSS files are imported. The files basically add up, so if we’re on a “M” breakpoint, it will load the base + XXS + XS + S + M in this case.

      These breakpoint files only contain CSS overrides, no base styles, and of course only overrides to that relevant breakpoint.

      As said, I like this approach because I never see media queries anywhere, I never duplicate them. I just work in the correct file. The other main benefit is that it gives a very good overview on the health of a project. For example, if I need to strongly override a single class in every single breakpoint, that class may not be very well designed from the start. Also, if one breakpoint SCSS files becomes overly large, it also smells like trouble.

      In the (very large) project that I am using this, most of these breakpoint files contain at most a few dozen lines of overrides, the exception being the breakpoint L, which brings radical layout changes and thus many overrides.

      The only downside I see with this approach is that some people may prefer to see all of a class (including breakpoint overrides) in one file. I don’t. I cleanly separate the base from the override.

      Does this help?

    • Ricardo Zea
      Permalink to comment#

      @Ferdy, yeah, that’s pretty cool.

      I have to admit I’d be one of those that would like to see a class and all its breakpoints in the same file :p

      The way you do this is pretty similar to how Chris here has shown before how he does it, where the main file is only a bunch of @imports. I know I’ve seen something similar here on CSS-Tricks.com but can’t find the page.

      OFFTOPIC –
      I am by no means a Sass expert and maybe you know this one already, but for those who don’t, you can concatenate all imported files in with single @import directive:

      @import 'compass', 'theme', 'mixins', 'init', 'main', 'map', 'content', 'modal', 'fullscreen', 'general', 'forms', 'nav';
      

      Or stacked for readability:

      @import 'compass',
              'theme',
              'mixins',
              'init',
              'main',
              'map',
              'content',
              'modal',
              'fullscreen',
              'general',
              'forms',
              'nav';
      

      Thanks for sharing and for the detailed explanation.

    • Tony
      Permalink to comment#

      Sorry I formatted the markdown wrong

      //----------------------------------*\
      // SCSS MIXINS
      //----------------------------------*/
      
      @import 'mixins/_vendor.scss';
      @import 'mixins/_position.scss';
      
      //----------------------------------*\
      // CONFIG MODULES
      //----------------------------------*/
      
      // Config modules
      @import 'modules/_vars.scss';
      @import 'modules/_clearfix.scss';
      @import 'modules/_normalize.scss';
      @import 'modules/_defaults.scss';
      
      //----------------------------------*\
      // PARTIALS
      //----------------------------------*/
      
      @import 'partials/_header.scss';
      @import 'partials/_nav.scss';
      @import 'partials/_sidebar.scss';
      @import 'partials/_main.scss';
      @import 'partials/_footer.scss';
      
      //----------------------------------*\
      // OBJECTS & MODULES
      //----------------------------------*/
      
      @import 'modules/_buttons.scss';
      @import 'modules/_inputs.scss';
      @import 'modules/_lists.scss';
      @import 'modules/_typography.scss';
      @import 'modules/_misc.scss';
      @import 'modules/_app.scss';
      
      // Enhancement modules
      @import 'modules/_breakpoints.scss';
      @import 'modules/_print.scss';
      
      //----------------------------------*\
      // VENDOR
      //----------------------------------*/
      
      @import 'vendor/_external.scss';
      
      
    • Tony
      Permalink to comment#

      Here is the Gruntfile I use:

      'use strict';
      
      /**
       * Livereload and connect variables
       */
      var LIVERELOAD_PORT = 35729;
      var lrSnippet = require('connect-livereload')({
        port: LIVERELOAD_PORT
      });
      var mountFolder = function (connect, dir) {
        return connect.static(require('path').resolve(dir));
      };
      
      /**
       * Grunt module
       */
      module.exports = function (grunt) {
      
        /**
         * Dynamically load npm tasks
         */
        require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
      
        grunt.initConfig({
      
          pkg: grunt.file.readJSON('package.json'),
      
          /**
           * Set project info
           */
          project: {
            src: 'src',
            app: 'app',
            assets: '<%= project.app %>/assets',
            css: [
              '<%= project.src %>/scss/style.scss'
            ],
            js: [
              '<%= project.src %>/js/*.js'
            ]
          },
      
          /**
           * Project banner
           * Dynamically appended to CSS/JS files
           * Inherits text from package.json
           */
          tag: {
            banner: '/*!\n' +
                    ' * <%= pkg.name %>\n' +
                    ' * <%= pkg.title %>\n' +
                    ' * <%= pkg.url %>\n' +
                    ' * @author <%= pkg.author %>\n' +
                    ' * @version <%= pkg.version %>\n' +
                    ' * Copyright <%= pkg.copyright %>. <%= pkg.license %> licensed.\n' +
                    ' */\n'
          },
      
          /**
           * Connect port/livereload
           * https://github.com/gruntjs/grunt-contrib-connect
           * Starts a local webserver and injects
           * livereload snippet
           */
          connect: {
            options: {
              port: 3333,
              hostname: '*'
            },
            livereload: {
              options: {
                middleware: function (connect) {
                  return [lrSnippet, mountFolder(connect, 'app')];
                }
              }
            }
          },
      
          /**
           * JSHint
           * https://github.com/gruntjs/grunt-contrib-jshint
           * Manage the options inside .jshintrc file
           */
          jshint: {
            files: ['src/js/*.js'],
            options: {
              jshintrc: '.jshintrc'
            }
          },
      
          /**
           * Concatenate JavaScript files
           * https://github.com/gruntjs/grunt-contrib-concat
           * Imports all .js files and appends project banner
           */
          concat: {
            dev: {
              files: {
                '<%= project.assets %>/js/scripts.min.js': '<%= project.js %>'
              }
            },
            options: {
              stripBanners: true,
              nonull: true,
              banner: '<%= tag.banner %>'
            }
          },
      
          /**
           * Uglify (minify) JavaScript files
           * https://github.com/gruntjs/grunt-contrib-uglify
           * Compresses and minifies all JavaScript files into one
           */
          uglify: {
            options: {
              banner: "<%= tag.banner %>"
            },
            dist: {
              files: {
                '<%= project.assets %>/js/scripts.min.js': '<%= project.js %>'
              }
            }
          },
      
          /**
           * Compile Sass/SCSS files
           * https://github.com/gruntjs/grunt-contrib-sass
           * Compiles all Sass/SCSS files and appends project banner
           */
          sass: {
            dev: {
              options: {
                style: 'expanded',
                banner: '<%= tag.banner %>'
              },
              files: {
                '<%= project.assets %>/css/style.min.css': '<%= project.css %>'
              }
            },
            dist: {
              options: {
                style: 'compressed',
                banner: '<%= tag.banner %>'
              },
              files: {
                '<%= project.assets %>/css/style.min.css': '<%= project.css %>'
              }
            }
          },
      
          /**
           * Opens the web server in the browser
           * https://github.com/jsoverson/grunt-open
           */
          open: {
            server: {
              path: 'http://localhost:<%= connect.options.port %>'
            }
          },
      
          /**
           * Runs tasks against changed watched files
           * https://github.com/gruntjs/grunt-contrib-watch
           * Watching development files and run concat/compile tasks
           * Livereload the browser once complete
           */
          watch: {
            concat: {
              files: '<%= project.src %>/js/{,*/}*.js',
              tasks: ['concat:dev', 'jshint']
            },
            sass: {
              files: '<%= project.src %>/scss/{,*/}*.{scss,sass}',
              tasks: ['sass:dev']
            },
            livereload: {
              options: {
                livereload: LIVERELOAD_PORT
              },
              files: [
                '<%= project.app %>/{,*/}*.html',
                '<%= project.assets %>/css/*.css',
                '<%= project.assets %>/js/{,*/}*.js',
                '<%= project.assets %>/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
              ]
            }
          }
        });
      
        /**
         * Default task
         * Run `grunt` on the command line
         */
        grunt.registerTask('default', [
          'sass:dev',
          'jshint',
          'concat:dev',
          'connect:livereload',
          'open',
          'watch'
        ]);
      
        /**
         * Build task
         * Run `grunt build` on the command line
         * Then compress all JS/CSS files
         */
        grunt.registerTask('build', [
          'sass:dist',
          'jshint',
          'uglify'
        ]);
      
      };
      
  12. Tony
    Permalink to comment#

    Off topic:
    chris I’m really digging the new look of css-tricks, it’s also loading a lot faster

  13. Teelah
    Permalink to comment#

    This is a great read, Media Queries is something that is just second nature now but now using it with SASS creates a little more complex code. I am very similar with my OCD with my CSS, so I want it to be clean and easy for people to understand.

  14. Darryl Snow
    Permalink to comment#

    Would love to see this super-mixin translated to Stylus, given that it’s better than Sass in every way.

    • Tony
      Permalink to comment#

      I’m a big fan of Stylus myself. Except I use it for my node applications, how do you use it in a non node project?

    • Darryl Snow
      Permalink to comment#

      I just use gulp-stylus to compile it to css

    • Alastair
      Permalink to comment#

      I’m interested in your claim that Stylus is better than Sass in every way. Can you expand on that a bit. What makes Stylus better? I haven’t ever used Stylus – I had a brief time with LESS but went back to Sass (SCSS).

    • tony
      Permalink to comment#

      doh >_< , can’t believe I didn’t even think of that lol, I didn’tthink of using it outside of a Node project until now Stylus is something that, I personally have used with express projects, it has a lot of the same syntax as old school SaSS. I love it’s syntax because it’s very rubesque. As far as being better, that’s a matter of opinion, I personally love it.

    • Darryl Snow
      Permalink to comment#

      Stylus can do everything that Sass can. The only reason to choose Sass over Stylus is if you reeeeeeaaaally love writing loads of extra curly braces, colons, semi-colons and the word “@mixin”.

    • Alastair
      Permalink to comment#

      I like the SCSS syntax, mainly because it’s familiar and consistent with the CSS I’ve been writing since around 1998! Old habits die hard :) And besides, my text editor does most of the work for me.

    • Ricardo Zea
      Permalink to comment#

      @Darryl Use SASS syntax then.

      I tend to use more @include than @mixin :]

  15. Steve
    Permalink to comment#

    I like to use JS to add a “lg, md, sm, or xs” class to my tag. Then I just nest that into my css rules so all my styling is together rather that broken up into a whole separate media query area.

This comment thread is closed. If you have important information to share, please contact us.