A Sass mixin that helps you compose media queries in an elegant way.

  • By null
  • Last update: Jan 5, 2023
  • Comments: 14
Awesome

Media Queries with superpowers Build Status

mq() is a Sass mixin that helps you compose media queries in an elegant way.

Here is a very basic example:

@use 'mq' as * with (
  $breakpoints: (
    mobile:  320px,
    tablet:  740px,
    desktop: 980px,
    wide:    1300px
  )
);

.foo {
  @include mq($from: mobile, $until: tablet) {
    background: red;
  }
  @include mq($from: tablet) {
    background: green;
  }
}

Compiles to:

@media (min-width: 20em) and (max-width: 46.24em) {
  .foo {
    background: red;
  }
}
@media (min-width: 46.25em) {
  .foo {
    background: green;
  }
}

Sass MQ was crafted in-house at the Guardian. Today, many more companies and developers are using it in their projects: see who uses Sass MQ.

How to use it

Immediately play with it on SassMeister: @use 'mq';.

OR:

  1. Install:

    • with Bower: bower install sass-mq --save
    • with npm: npm install sass-mq --save supports eyeglass
    • with yarn: yarn add sass-mq supports eyeglass

    OR Download _mq.scss into your Sass project.

  2. Import the partial in your Sass files and override default settings with your own preferences:

// Name your breakpoints in a way that creates a ubiquitous language
// across team members. It will improve communication between
// stakeholders, designers, developers, and testers.
$breakpoints: (
  mobile: 320px,
  tablet: 740px,
  desktop: 980px,
  wide: 1300px,
  // Tweakpoints
  desktopAd: 810px,
  mobileLandscape: 480px,
);

// If you want to display the currently active breakpoint in the top
// right corner of your site during development, add the breakpoints
// to this list, ordered by width. For examples: (mobile, tablet, desktop).
$breakpoints-shown: (mobile, mobileLandscape, tablet, desktop, wide);

@use 'path/to/mq' with (
  $breakpoints: $breakpoints,
  $show-breakpoints: $breakpoints-shown,
);

Notes about @use Vs @import

When using the @use directive, you have to change your mindset when working with vars, functions or mixins and how they are now seen by Sass.

Previously, with the @import statement any var, function, or mixin were exposed in the global scope. That means that you could define a var like $mq-media-type: all in your main sass file and use it anywhere as long as the main file had been loaded previously.

This was possible because vars, functions, and mixins were set in the global scope.

One drawback of this behaviour was that we needed to ensure not to pollute the global scope with common names or names that may be already taken by any other library.

To solve this matter, we mostly used a prefix in vars, functions, or mixins in order to avoid collapsing names.

Now with the new @use directive, no var, function, or mixin is placed in global scope, and they are all scoped within the file.

That means that we explicitly need to include the partial file in each file that may use its vars, functions or mixins (similar to ES6 import modules).

So, previously we could have a typical setup like this:

// main.scss
@import 'mq';
@import 'typography';
@import 'layout';
@include mq($from:tablet) {
  ...
}

...

// typography.scss
@include mq($from:tablet) {
  ...
}

Now, you will need to explicitly import the _mq.scss file in each file that needs to use any var, function or mixin from it:

// main.scss
@use 'mq';
@use 'typography';
@use 'layout';
@include mq.mq($from:tablet) {
  ...
}
...

// typography.scss
@use 'mq';
@include mq.mq($from:tablet) {
  ...
}

Other important things about @use:

  • The file is only imported once, no matter how many times you @use it in a project.

  • Variables, mixins, and functions (what Sass calls “members”) that start with an underscore (_) or hyphen (-) are considered private, and not imported.

  • Members from the used file are only made available locally, but not passed along to future imports.

  • Similarly, @extends will only apply up the chain; extending selectors in imported files, but not extending files that import this one.

  • All imported members are namespaced by default.

Please see introducing-sass-modules for more info about sass modules.

  1. Play around with mq() (see below)

Responsive mode

mq() takes up to three optional parameters:

  • $from: inclusive min-width boundary
  • $until: exclusive max-width boundary
  • $and: additional custom directives

Note that $until as a keyword is a hard limit i.e. it's breakpoint - 1.

@use 'mq';

.responsive {
  // Apply styling to mobile and upwards
  @include mq.mq($from: mobile) {
    color: red;
  }
  // Apply styling up to devices smaller than tablets (exclude tablets)
  @include mq.mq($until: tablet) {
    color: blue;
  }
  // Same thing, in landscape orientation
  @include mq.mq($until: tablet, $and: '(orientation: landscape)') {
    color: hotpink;
  }
  // Apply styling to tablets up to desktop (exclude desktop)
  @include mq.mq(tablet, desktop) {
    color: green;
  }
}

Verbose and shorthand notations

Sometimes you’ll want to be extra verbose (for example, if you’re developing a library based on top of sass-mq), however for readability in a codebase, the shorthand notation is recommended.

All of these examples output the exact same thing and are here for reference, so you can use the notation that best matches your needs:

@use 'mq';
// Verbose
@include mq.mq(
  $from: false,
  $until: desktop,
  $and: false,
  $media-type: $media-type // defaults to 'all'
) {
  .foo {
  }
}

// Omitting argument names
@include mq.mq(false, desktop, false, $media-type) {
  .foo {
  }
}

// Omitting tailing arguments
@include mq(false, desktop) {
  .foo {
  }
}

// Recommended
@include mq($until: desktop) {
  .foo {
  }
}

See the detailed API documentation

Adding custom breakpoints

@include add-breakpoint(tvscreen, 1920px);

.hide-on-tv {
  @include mq(tvscreen) {
    display: none;
  }
}

Seeing the currently active breakpoint

While developing, it can be nice to always know which breakpoint is active. To achieve this, set the $show-breakpoints variable to be a list of the breakpoints you want to debug, ordered by width. The name of the active breakpoint and its pixel and em values will then be shown in the top right corner of the viewport.

// Adapt the list to include breakpoint names from your project
$show-breakpoints: (phone, phablet, tablet);

$show-breakpoints

Changing media type

If you want to specify a media type, for example to output styles for screens only, set $media-type:

SCSS

@use 'mq' with ($media-type: screen);

.screen-only-element {
  @include mq.mq(mobile) {
    width: 300px;
  }
}

CSS output

@media screen and (max-width: 19.99em) {
  .screen-only-element {
    width: 300px;
  }
}

Implementing sass-mq in your project

Please see the examples folder which contains a variety of examples on how to implement "sass-mq"

Backward compatibility with @import

Just in case you need to have backward compatibility and want to use@import instead of @use, you can do so by importing _mq.import.scss instead of _mq.scss.

Please see legacy.scss on examples folder.

Running tests

npm test

Generating the documentation

Sass MQ is documented using SassDoc.

Generate the documentation locally:

sassdoc .

Generate & deploy the documentation to http://sass-mq.github.io/sass-mq/:

npm run sassdoc

Inspired By…

On Mobile-first CSS With Legacy Browser Support

Who uses Sass MQ?

Sass MQ was developed in-house at the Guardian.

These companies and projects use Sass MQ:


Looking for a more advanced sass-mq, with support for height and other niceties?
Give @mcaskill's fork of sass-mq a try.

Github

https://github.com/sass-mq/sass-mq

Comments(14)

  • 1

    Dart sass 1.34/version bump

    New bump Version.

    • Compatible only with dart-sass
    • Please see CHANGELOG.md to see all the changes

    TODO

    I add about 22 jest test to check for functionality, but I left in this MR old test to decide if we should keep them or remove them along with their dependencies to eyeglass. Since they are now all cover with jest test and sass-true package and this it will be a good idea to remove them

  • 2

    Add diamond support

    Following up on our conversation via email I have added diamond support. I didn't know if you wanted the package to be named sass-mq like npm and bower or mq as you refer to it in the readme.

    To publish the package when you are ready, run the following commands: Install diamond: npm i -g diamondpkg

    Register for an account: diamond login

    Publish the package (run in package dir) diamond publish

  • 3

    Ability to show active breakpoint in corner of viewport

    A little bonus feature that I've found quite useful for debugging and cross-device testing: Flip a switch to show the active breakpoint in the top right corner of the viewport.

    show-breakpoints

  • 4

    Add the ability to override width calculation - Closes #64

    This PR is an answer to #64 and #59. Thanks to @fvch, @davidhund, @shaunbent and @smbeiragh for their input.

    I want to give authors the ability to use whatever unit they want, but I also really want to keep ems as the very opinionated default.

    Instead of giving an option between ems/rems/px, I'm allowing developers to tweak sass-mq at its core by overriding the width calculation.

    Install the new beta:

    npm i sass-mq@^3.3.0 --save

    or

    bower i sass-mq@^3.3.0 --save

    Let me know what you think!

    Todo

    • [x] Proof of concept to override widths calculations
    • [x] Write tests for mq-width and mq-width-override

    Keep all media queries in px

    /// Override width calculation
    /// to use pixels instead of ems
    @function mq-width-override($px) {
        @if unitless($px) {
            @return $px * 1px;
        }
        @if unit($px) == em {
            @return $px / 1em * $mq-base-font-size;
        }
        @return $px;
    }
    
    @import 'path/to/_mq.scss';
    

    Convert media queries to rem

    /// Override width calculation
    /// to use rems instead of ems
    @function mq-width-override($rem) {
        @if unitless($rem) {
            @return $rem * 1px / $mq-base-font-size * 1rem;
        }
        @if unit($rem) == em {
            @return $rem / 1em * $mq-base-font-size / 1px * 1rem;
        }
        @if unit($rem) == px {
            @return $rem / $mq-base-font-size * 1rem;
        }
        @return $rem;
    }
    
    @import 'path/to/_mq.scss';
    
  • 5

    sass-mq v4: Sort added breakpoints

    Fixes #101 - Thanks to @solleer for raising this issue!

    This pull request is the new sass-mq v4

    This PR contains a small breaking change that should only affect a few users.

    ⭐️ Upgrading to v4.0.0 is recommended ⭐️

    How to test v4.0.0 beta in your project:

    npm install sass-mq@beta
    
    # or, if you use yarn
    yarn add sass-mq@beta
    
    # and, if you use bower
    bower install [email protected]
    

    You can also download the latest _mq.scss into your project.

    Before

    New breakpoints added via mq-add-breakpoint are simply appended to $mq-breakpoints:

    $mq-breakpoints: (
        mobile: 300px,
        tablet: 620px
    );
    
    @import 'mq';
    
    @include mq-add-breakpoint(tiny, 160px);
    @include mq-add-breakpoint(medium, 500px);
    
    // Results in:
    // $mq-breakpoints: (
    //     mobile: 300px,
    //     tablet: 620px,
    //     tiny:   160px, < the smallest breakpoint should be first
    //     medium: 500px < this breakpoint should be third
    // );
    

    After

    New breakpoints added via mq-add-breakpoint keeps $mq-breakpoints ordered from the smallest to the largest breakpoint:

    $mq-breakpoints: (
        mobile: 300px,
        tablet: 620px
    );
    
    @import 'mq';
    
    @include mq-add-breakpoint(tiny, 160px);
    @include mq-add-breakpoint(medium, 500px);
    
    // Results in:
    // $mq-breakpoints: (
    //     tiny:   160px,
    //     mobile: 300px,
    //     medium: 500px,
    //     tablet: 620px
    // );
    

    Thanks to @snugug for providing a quick-sort function!

  • 6

    Stop converting media queries to em

    Hi,

    sass-mq currently converts all breakpoints to em values, using a ratio of 1em = 16px (that ratio can be changed but it would definitely create unexpected behavior for users, so it's a good thing the variable for that is not advertised). The rationale for converting to em was browser limitations (January 2014 post) with zooming. More specifically, it was a WebKit bug, affecting Chrome and Safari, which has since been fixed (around 2014?).

    No zoom issue with px media queries in my tests on:

    • Recent and older Firefoxes
    • IE 9, IE 11
    • Chrome 30
    • Safari 8, Safari 6.1 on OS X

    So it seems safe to say it's a non-issue now. What's more, using em media queries as substitutes for pixels can pose a risk if end users have changed the browser's base font size to something that is not 16px (to be confirmed; not sure if it does impact em-based media queries in all browsers).

    I suggest that sass-mq stop converting pixels to ems and allow users to set breakpoints in ems in $mq-breakpoints (if that is not already the case). For max-width queries, a pixel breakpoint should be reduced by one, e.g. (max-width: 799px) if the breakpoint is 800px.

  • 7

    Warning using / for division is deprecated

    Updating sass to version 1.34.0 I get this warning from sass:

    EPRECATION WARNING: Using / for division is deprecated and will be removed in Dart Sass 2.0.0.
    Recommendation: math.div($px, $base-font-size)
    More info and automated migrator: https://sass-lang.com/d/slash-div
    
     │     @return ($px / $base-font-size) * 1em;
     │              ^^^^^^^^^^^^^^^^^^^^^
    
        node_modules\sass-mq\_mq.scss 111:14   mq-px2em()
        node_modules\sass-mq\_mq.scss 198:25   mq()
    
  • 8

    Redesign and update library

    TL;RD : redesign and update library

    Motivation

    Deprecation of @import and global vars and addition of new functionalities

    with the addition of new functionalities on dart sass compiler , this library starts to get deprecation warnings and will probably stop working by October 2022.

    Read more in this post

    One year after this deprecation goes into effect (1 October 2022 at latest), we will drop support for @import and most global > functions entirely. This will involve a major version release for all implementations

    Abandon of sass engines other than dart-sass

    Also since now there were 3 different sass compilers but it look that now there will be only one maintained (dart sass), so this makes unnecessary to maintain test and compatibility with the other two sass engines:

    Current Releases for sass engines are: Dart Sass 1.34.0: Dart Sass is the primary implementation of Sass, which means it gets new features before any other implementation. Also currently is the only one maintain

    LibSass 3.6.5: LibSass is Deprecated

    Ruby Sass ⚰ Ruby Sass was the original implementation of Sass, but it reached its end of life as of 26 March 2019. It's no longer supported, and Ruby Sass users should migrate to another implementation.

    Drop compatibility of old browsers

    This library has some backward compatibilities with very, very old browsers (IE8) that didn't support media queries and it looks reasonable that we could get read of this compatibility

    Redesign

    Whit all these things said, it looks that a redesign / rethinking of this library may need it to be done

    Checkpoints

    Bellow I will list some checkpoint that I detect that can be done in this library, probably there are some more and maybe not all these points are the best approach, I just put it here to discuss them.

    • [ ] Remove $mq-base-font-size since it is deprecated
    • [ ] Remove $mq-responsive (Drop compatibility of old browsers)
    • [ ] $mq-breakpoints maybe we can use build in module sass.map
    • [ ] global functions will not be allow in the future so we should change thinks like map-has-key(..) in favor of map.has-key(...)
    • [ ] drop test for sass engines other than dart-sass
    • [ ] regenerate readme and docs with ifn about using @use
    • [ ] add compatibility for using this library with @import instead of @use (this can be easily done by using @forward directive)

    Bibliography

    the module system is launched libsass is deprecated sass built in modules

  • 9

    Subpixel issue in FF and IE11

    After extensive testing, I believe this value should go down one more decimal for it to work perfectly with FF and IE11. It should be 0.001em instead of 0.01em.

    https://github.com/sass-mq/sass-mq/blob/47cd6b1156d35a702edd3fbed0bcd285dcd95d29/_mq.scss#L195

  • 10

    Add an option for overriding default media type in queries

    Membership have had a few issues with media queries applying to all. I'd like to add this new option, $mq-media-type (which defaults to all) to allow us to override this.

  • 11

    Error: media query expression must begin with '('

    I'm getting this error with the latest version of node:

    Error: my/path/to/_mq.scss
    218:50  media query expression must begin with '('
    

    I'm not even sure what dependency might be at fault here, so I'm sorry if I'm way off. Any ideas about why this might be occurring?

  • 12

    Bump minimatch from 3.0.4 to 3.1.2

    Bumps minimatch from 3.0.4 to 3.1.2.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

  • 13

    Basic container query support

    Perhaps this is out of scope of this library, but it would be lovely to have some kind of container query support in sass-mq. Use it exclusively and have for years, so would be awesome to not have to use @container syntax directly just like I don't have to with @media.

    There's loads more syntax to be explored should there be an interest in supporting them fully, especially around things like: Do we apply container-type automatically somehow? Do we support style queries?

    Anyway, this is a really basic implementation that allows rudimentary usage:

    .card {
        container-type: inline-size;
    
        @include mq($until: 400px, $container: true) {
            &__image {
                // Some stlyes
            }
        }
    }
    

    or

    .card {
        container-type: inline-size;
    
         &__image {
            @include mq($until: 400px, $container: true) {
                // Some stlyes
            }
        }
    }
    
  • 14

    Using / for division outside of calc() is deprecated and will be removed in Dart Sass 2.0.0

    Hi,

    Using sass-mq with Dart Sass gives the following deprecation warning:

    Deprecation Warning: Using / for division outside of calc() is deprecated and will be removed in Dart Sass 2.0.0.

    Since locally editing "_mq.scss" in my node_modules isn't a good long-term solution (Also: it's a shared project so...) Is there any way, outside of editing "_mq.scss", to remove/avoid this warning?

    I would like to keep using sass-mq but also use the latest sass and other tools. Ok, it's just a warning but it would be nicer/cleaner/better to have no (deprecation) warnings or errors at all.

    Some extra info: node: 16 sass-mq: ^5.0.0 sass: ^1.52.3

    Thanks in advance!