Skip to content

Instantly share code, notes, and snippets.

@wfendler
Last active December 19, 2015 20:48
Show Gist options
  • Save wfendler/6015412 to your computer and use it in GitHub Desktop.
Save wfendler/6015412 to your computer and use it in GitHub Desktop.
// FILE: _mq-mixin-modern.scss
//
// standard media-query mixin with set breakpoints and the option for px-based min-width value
@mixin media-query($media-query){
@if $media-query == "hand-and-up" {
@media only screen and (min-width: $hand-start) { @content; }
}
@else if $media-query == "lap-and-up" {
@media only screen and (min-width: $lap-start) { @content; }
}
@else if $media-query == "desk-and-up" {
@media only screen and (min-width: $desk-start) { @content; }
}
@else if $media-query == "wall-and-up" {
@media only screen and (min-width: $wall-start) { @content; }
}
@else if $media-query == "palm-and-down" {
@media only screen and (max-width: ($hand-start - 1px)) { @content; }
}
// If $media-query arg is not named, it expects a px value to set a min-width
// this should be used for all feature-based breakpoints
@else {
@media only screen and (min-width: ($media-query)) { @content; }
}
}
// FILE: _mq-mixin-legacy.scss
//
// sets media-query mixin to not include any media queries, it outputs all media queries small -> big to have proper cascade
@mixin media-query($media-query){
@if $media-query == "hand-and-up" {
@content;
}
@else if $media-query == "lap-and-up" {
@content;
}
@else if $media-query == "desk-and-up" {
@content;
}
// Exclude wall and up, we'll assume IE won't be that huge.
// Exclude "palm-and-down"
// All feature/px-based breakpoints
// This may cause an improper cascade if used in conjunction with the named breakpoints above,
// but it's usually one or the other, not both.
@else {
@content;
}
}
// FILE: main.scss
@import "_mq-mixin-modern";
@import "other-partials";
// FILE: main-ie.scss
@import "_mq-mixin-legacy";
@import "other-partials";
// ^ Compile these separately and your HTML will need to look like this:
//
// <!--[if (gt IE 8) | (IEMobile)]><!-->
// <link href="main.css">
// <!--<![endif]-->
//
// <!--[if (lt IE 9) & (!IEMobile)]>
// <link href="main-ie.css">
// <![endif]-->
@wfendler
Copy link
Author

It causes some duplicate rules as the media-queries override themselves, but it will all be cascaded properly and the file will not be downloaded twice. The other downside is maintaining two "main" stylesheets, but that is a minor detail for how big of a maintainability win this has.

@wfendler
Copy link
Author

The duplication downside:

.page-nav {
  width: 100%;
  @include media-query(palm-and-up) {
    width: 80%;
  }
  @include media-query(desk-and-up) {
    width: 50%;
  }
}

Would give this output in main-ie.css

.page-nav {
  width: 100%;
  width: 80%;
  width: 50%;
}

@wfendler
Copy link
Author

The potential issue with the px based breakpoint:

// assuming lap-and-up puts out a media query with a value greater than 400px...
.page-nav {
  width: 100%;
  @include media-query(lap-and-up) {
    width: 50%;
  }
  @include media-query(400px) {
    width: 80%;
  }
}

Would give us this output in main-ie.css

.page-nav {
  width: 100%;
  width: 50%;
  width: 80%;
}

The 400px breakpoint would work properly inside of a media query, but in main-ie.css the cascade would be incorrect. So, the only risk is using px-based breakpoint values in the incorrect order in your .scss files. This however should never be a problem since it would be general bad-practice to author your sass media queries in a different order than they would cascade.

@cstoobes
Copy link

// FILE: _mixins.scss

@if $legacy-ie == true {

@mixin media-query($media-query){

@if $media-query == "hand-and-up" { @content; }

@else if $media-query == "lap-and-up" { @content; }

@else if $media-query == "desk-and-up" { @content; }

// Exclude "wall and up", "hand-and-down"

// All custom breakpoints
@else {
  @content;
}

}
}
@else {
@mixin media-query($media-query){

@else if $media-query == "hand-and-up" {
  @media only screen and (min-width:$hand-start) { @content; }
}

@else if $media-query == "lap-and-up" {
  @media only screen and (min-width:$lap-start) { @content; }
}

@else if $media-query == "desk-and-up" {
  @media only screen and (min-width:$desk-start) { @content; }
}

@else if $media-query == "wall-and-up" {
  @media only screen and (min-width: $wall-start) { @content; }
}

@else if $media-query == "hand-and-down" {
  @media only screen and (max-width: ($hand-start - 1px)) { @content; }
}

// custom breakpoints
@else {
  @media only screen and ($media-query) { @content; }
}

}
}

// FILE: main.scss
$legacy-ie: false;
@import "mixins";

// FILE: main-ie.scss
$legacy-ie: true;
@import "mixins";

@wfendler
Copy link
Author

Set a flag for the mixin to let it know if it should utilize the Modernizr .no-mediaqueries class

The new mixin:

@mixin respond-to( $mediaQuery, $fallback ){

  @if $fallback == true {
    @media $mediaQuery {
      @content;
    }
    .no-mediaqueries & {
      @content;
    }
  }

  @else {
    @media $mediaQuery {
      @content;
    }
  }

}

Here it is in use:

.object {
  font-size: 12px;
  @include respond-to("only screen and (min-width: 600px)", true) {
    font-size: 16px;
  }
}

The output would look like this:

.object { font-size: 12px; }

@media only screen and (min-width: 600px) {
  .object { font-size: 16px; }
}

.no-mediaqueries .object { font-size: 16px; }

@wfendler
Copy link
Author

The previous one duplicates too much code within the main stylesheet. Probably too costly to be useful, although it is nice it relies on the modernizr class for feature detecting instead of just serving the "non-mediaquery" styles to IE alone.

Currently, the best route might be two "main" stylesheets, one for IE that includes a flag to tell the media query mixin to not print out the actual media queries.

// Although it is very simple, it allows for the most flexible mixin use.
// So we set some variables for our common breakpoints.

$hand-start: 520px;
$lap-start:  727px;
$desk-start: 1024px;
$wall-start: 1200px;

$palm-and-down: "only screen and (max-width: " + ($hand-start - 1px)+ ")";
$hand-and-up: "only screen and (min-width:" + $hand-start + ")";
$lap-and-up: "only screen and (min-width:" + $lap-start+ ")";
$desk-and-up: "only screen and (min-width:" + $desk-start+ ")";
$wall-and-up: "only screen and (min-width:" + $wall-start+ ")";


@mixin respond-to( $mediaQuery, $fallback ){

  @if $legacy-ie == true {
    @content;
  }

  @else {
    @media $mediaQuery {
      @content;
    }
  }

}


// It can then be used like this
.feature {
  font-size: 14px;

  @include respond-to( $lap-and-up ){
    font-size: 20px;
  }

  @include respond-to( "only screen and (min-height: 700px" ) {
    margin-bottom: .5em;
  }
}

main.css

$legacy-ie: false;
@import "mixins";
// all other imports after this

main-legacy.css

$legacy-ie: true;
@import "mixins";
// all other imports after this

@cstoobes
Copy link

Had to rearrange the media query mixin so it would build correctly.

@mixin media-query($media-query){

@if $legacy-ie == true {
//render styles without media queries

@if $media-query == "hand-and-up" { @content; }

@else if $media-query == "lap-and-up" { @content; }

@else if $media-query == "desk-and-up" { @content; }

// exclude "wall and up", "hand-and-down"

// include custom breakpoints
@else {
  @content;
}

}
@else {

@if $media-query == "hand-and-up" {
  @media only screen and (min-width:$hand-start) { @content; }
}

@else if $media-query == "lap-and-up" {
  @media only screen and (min-width:$lap-start) { @content; }
}

@else if $media-query == "desk-and-up" {
  @media only screen and (min-width:$desk-start) { @content; }
}

@else if $media-query == "wall-and-up" {
  @media only screen and (min-width: $wall-start) { @content; }
}

@else if $media-query == "hand-and-down" {
  @media only screen and (max-width: ($hand-start - 1px)) { @content; }
}

// custom breakpoints
@else {
  @media only screen and ($media-query) { @content; }
}

}
}

main.css

$legacy-ie: false;
@import "mixins";
// all other imports after this

main-legacy.css

$legacy-ie: true;
@import "mixins";
// all other imports after this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment