From 5b3b01d03007a1f53f8884730b3b94a6b5a5370d Mon Sep 17 00:00:00 2001 From: Daniel Tonon Date: Wed, 6 Sep 2017 21:58:24 +1000 Subject: [PATCH 01/13] Some buggy starting code for the feature I'm aiming to use "plus" as the keyword for signifying that queries should both be true --- _functions.scss | 5 +++ _mq.scss | 101 ++++++++++++++++++++++++++++++------------------ 2 files changed, 69 insertions(+), 37 deletions(-) diff --git a/_functions.scss b/_functions.scss index f0d07ea..539b1a6 100644 --- a/_functions.scss +++ b/_functions.scss @@ -51,6 +51,11 @@ @return $result; } +// https://github.com/sass/sass/issues/543 +@function contains($list, $var) { + @return (null == index($list, $var)); +} + //For getting the number values in ratio based ranges @function get-start($string){ $string: unquote($string); diff --git a/_mq.scss b/_mq.scss index ab8814f..e429bca 100644 --- a/_mq.scss +++ b/_mq.scss @@ -118,56 +118,83 @@ $mq-em-base: 16px !default; @return $mediaString; } -@mixin mq($range, $setting1: null, $setting2: null) { +@function isBasicQuery($range) { + @return type-of(nth($range,1)) == string; +} - @if (length($range) > 1) { +@function calculateBasicQuery($mqSet){ + $newRange: nth($mqSet,1); + $newWidth1: nth($mqSet,2); + $newWidth2: null; + @if (length($mqSet) == 3){ + $newWidth2: nth($mqSet,3); + } - //Uses this functionality if using a single level MQ variable - //eg: - //$MQ-example--single: inside, 1000px, 500px; - //@include mq($MQ-example--single){} - @if (type-of(nth($range,1)) == string){ - - $newRange: nth($range,1); - $newWidth1: nth($range,2); - $newWidth2: null; - @if (length($range) == 3){ - $newWidth2: nth($range,3); - } + @return calculateMQ($newRange, $newWidth1, $newWidth2); +} - @media #{calculateMQ($newRange, $newWidth1, $newWidth2)} { - @content; - } +@function joinQueries($range){ + $isAndStatment: contains($range, 'plus'); + //Uses this functionality if using a single level MQ variable + //eg: + //$MQ-example--single: inside, 1000px, 500px; + //@include mq($MQ-example--single){} + @if (isBasicQuery($range)){ - //Uses this functionality if using a multi level MQ variable - //eg: - //$MQ-example--multiple: - // (inside, 1000px, 500px), - // (min, 1200px) - //; - //@include mq($MQ-example--multiple){} - } @else { - $mediaQuery: (); + @return calculateBasicQuery($range); - @each $mqSet in $range { + //Uses this functionality if using a multi level MQ variable + //eg: + //$MQ-example--multiple: + // (inside, 1000px, 500px), + // (min, 1200px) + //; + //@include mq($MQ-example--multiple){} + } @else { - $newRange: nth($mqSet,1); - $newWidth1: nth($mqSet,2); - $newWidth2: null; - @if (length($mqSet) == 3){ - $newWidth2: nth($mqSet,3); - } + $queryString: ''; + + @each $mqSet in $range { + // @debug ('mqSet:' $mqSet); + + @if ($mqSet != 'plus') { + @if (isBasicQuery($mqSet)){ - $mediaQuery: append($mediaQuery, calculateMQ($newRange, $newWidth1, $newWidth2), 'comma'); + $newQuery: calculateBasicQuery($mqSet); - }//end @each + @if ($isAndStatment) { + @if ($queryString == ''){ + $queryString: $newQuery; + } @else { + $queryString: $queryString + ' and ' + $newQuery; + } + } @else { + $queryString: append($queryString, $newQuery, 'comma'); + } - @media #{$mediaQuery} { - @content; + } @else { + $queryString: $queryString + joinQueries($mqSet); + } } } + @debug $queryString; + + @return $queryString; + } +} + +@mixin mq($range, $setting1: null, $setting2: null) { + + @debug $range; + + @if (length($range) > 1) { + + @media #{joinQueries($range)} { + @content; + } + //Uses this functionality if placing MQ data inline //eg: //@include mq(inside, 1000px, 500px); From a08bffc926b4e2433e1d0c7c3ecf7f5a89db9093 Mon Sep 17 00:00:00 2001 From: Daniel Tonon Date: Wed, 6 Sep 2017 22:13:06 +1000 Subject: [PATCH 02/13] it is now compiling but not producing the correct result --- _mq.scss | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/_mq.scss b/_mq.scss index e429bca..bcdde09 100644 --- a/_mq.scss +++ b/_mq.scss @@ -153,10 +153,10 @@ $mq-em-base: 16px !default; //@include mq($MQ-example--multiple){} } @else { - $queryString: ''; + $queryString: (); @each $mqSet in $range { - // @debug ('mqSet:' $mqSet); + @debug ('mqSet:', $mqSet); @if ($mqSet != 'plus') { @if (isBasicQuery($mqSet)){ @@ -168,9 +168,11 @@ $mq-em-base: 16px !default; $queryString: $newQuery; } @else { $queryString: $queryString + ' and ' + $newQuery; + @debug ('and method' $queryString); } } @else { $queryString: append($queryString, $newQuery, 'comma'); + @debug ('or method' $queryString); } } @else { @@ -187,8 +189,6 @@ $mq-em-base: 16px !default; @mixin mq($range, $setting1: null, $setting2: null) { - @debug $range; - @if (length($range) > 1) { @media #{joinQueries($range)} { From 41baddcb11a31f904df1aaf3f74458fd77da50c7 Mon Sep 17 00:00:00 2001 From: Daniel Tonon Date: Wed, 6 Sep 2017 22:38:46 +1000 Subject: [PATCH 03/13] It is now working as expected I think --- _functions.scss | 5 ----- _mq.scss | 59 ++++++++++++++++++++++++++++--------------------- 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/_functions.scss b/_functions.scss index 539b1a6..f0d07ea 100644 --- a/_functions.scss +++ b/_functions.scss @@ -51,11 +51,6 @@ @return $result; } -// https://github.com/sass/sass/issues/543 -@function contains($list, $var) { - @return (null == index($list, $var)); -} - //For getting the number values in ratio based ranges @function get-start($string){ $string: unquote($string); diff --git a/_mq.scss b/_mq.scss index bcdde09..538d14a 100644 --- a/_mq.scss +++ b/_mq.scss @@ -85,34 +85,34 @@ $mq-em-base: 16px !default; $mediaString: map-get(( //*1 value given*/ - min : 'screen and (min-width: #{$setting1Plus})', - max : 'screen and (max-width: #{$setting1})', + min : '(min-width: #{$setting1Plus})', + max : '(max-width: #{$setting1})', - min-height : 'screen and (min-height: #{$setting1Plus})', - max-height : 'screen and (max-height: #{$setting1})', + min-height : '(min-height: #{$setting1Plus})', + max-height : '(max-height: #{$setting1})', - ratio : 'screen and (aspect-ratio: #{$setting1})', - min-ratio : 'screen and (min-aspect-ratio: #{$setting1})', - max-ratio : 'screen and (max-aspect-ratio: #{$setting1})', + ratio : '(aspect-ratio: #{$setting1})', + min-ratio : '(min-aspect-ratio: #{$setting1})', + max-ratio : '(max-aspect-ratio: #{$setting1})', - device-ratio : 'screen and (device-aspect-ratio: #{$setting1})', - min-device-ratio : 'screen and (min-device-aspect-ratio: #{$setting1})', - max-device-ratio : 'screen and (max-device-aspect-ratio: #{$setting1})', + device-ratio : '(device-aspect-ratio: #{$setting1})', + min-device-ratio : '(min-device-aspect-ratio: #{$setting1})', + max-device-ratio : '(max-device-aspect-ratio: #{$setting1})', - orientation : 'screen and (orientation: #{$setting1})', + orientation : '(orientation: #{$setting1})', //*2 values given*/ - inside : 'screen and (max-width: #{$setting1}) and (min-width: #{$setting2Plus})', - outside : 'screen and (max-width: #{$setting2}), screen and (min-width: #{$setting1Plus})', + inside : '(max-width: #{$setting1}) and (min-width: #{$setting2Plus})', + outside : '(max-width: #{$setting2}), screen and (min-width: #{$setting1Plus})', - inside-height : 'screen and (max-height: #{$setting1}) and (min-height: #{$setting2Plus})', - outside-height : 'screen and (max-height: #{$setting2}), screen and (min-height: #{$setting1Plus})', + inside-height : '(max-height: #{$setting1}) and (min-height: #{$setting2Plus})', + outside-height : '(max-height: #{$setting2}), screen and (min-height: #{$setting1Plus})', - inside-ratio : 'screen and (max-aspect-ratio: #{$setting1}) and (min-aspect-ratio: #{$setting2})', - outside-ratio : 'screen and (max-aspect-ratio: #{$setting2}), screen and (min-aspect-ratio: #{$setting1})', + inside-ratio : '(max-aspect-ratio: #{$setting1}) and (min-aspect-ratio: #{$setting2})', + outside-ratio : '(max-aspect-ratio: #{$setting2}), screen and (min-aspect-ratio: #{$setting1})', - inside-device-ratio : 'screen and (max-device-aspect-ratio: #{$setting1}) and (min-device-aspect-ratio: #{$setting2})', - outside-device-ratio : 'screen and (max-device-aspect-ratio: #{$setting2}), screen and (min-device-aspect-ratio: #{$setting1})', + inside-device-ratio : '(max-device-aspect-ratio: #{$setting1}) and (min-device-aspect-ratio: #{$setting2})', + outside-device-ratio : '(max-device-aspect-ratio: #{$setting2}), screen and (min-device-aspect-ratio: #{$setting1})', ), $range); @return $mediaString; @@ -134,7 +134,16 @@ $mq-em-base: 16px !default; } @function joinQueries($range){ - $isAndStatment: contains($range, 'plus'); + + $isAndStatment: false; + + @each $statement in $range { + @if ($statement == 'plus') { + $isAndStatment: true; + } + } + + @debug $isAndStatment; //Uses this functionality if using a single level MQ variable //eg: @@ -142,7 +151,7 @@ $mq-em-base: 16px !default; //@include mq($MQ-example--single){} @if (isBasicQuery($range)){ - @return calculateBasicQuery($range); + @return 'screen and ' + calculateBasicQuery($range); //Uses this functionality if using a multi level MQ variable //eg: @@ -164,14 +173,14 @@ $mq-em-base: 16px !default; $newQuery: calculateBasicQuery($mqSet); @if ($isAndStatment) { - @if ($queryString == ''){ - $queryString: $newQuery; + @if ($queryString == ()){ + $queryString: 'screen and ' + $newQuery; } @else { $queryString: $queryString + ' and ' + $newQuery; @debug ('and method' $queryString); } } @else { - $queryString: append($queryString, $newQuery, 'comma'); + $queryString: append($queryString, 'screen and ' + $newQuery, 'comma'); @debug ('or method' $queryString); } @@ -200,7 +209,7 @@ $mq-em-base: 16px !default; //@include mq(inside, 1000px, 500px); } @else { - @media #{calculateMQ($range, $setting1, $setting2)} { + @media #{'screen and ' + calculateMQ($range, $setting1, $setting2)} { @content; } } From bb9391f1f2d2f098cb90c0bf00c585b915a18f90 Mon Sep 17 00:00:00 2001 From: Daniel Tonon Date: Wed, 6 Sep 2017 22:52:38 +1000 Subject: [PATCH 04/13] Removed debug statements --- _mq.scss | 7 ------- 1 file changed, 7 deletions(-) diff --git a/_mq.scss b/_mq.scss index 538d14a..db42b91 100644 --- a/_mq.scss +++ b/_mq.scss @@ -143,8 +143,6 @@ $mq-em-base: 16px !default; } } - @debug $isAndStatment; - //Uses this functionality if using a single level MQ variable //eg: //$MQ-example--single: inside, 1000px, 500px; @@ -165,7 +163,6 @@ $mq-em-base: 16px !default; $queryString: (); @each $mqSet in $range { - @debug ('mqSet:', $mqSet); @if ($mqSet != 'plus') { @if (isBasicQuery($mqSet)){ @@ -177,11 +174,9 @@ $mq-em-base: 16px !default; $queryString: 'screen and ' + $newQuery; } @else { $queryString: $queryString + ' and ' + $newQuery; - @debug ('and method' $queryString); } } @else { $queryString: append($queryString, 'screen and ' + $newQuery, 'comma'); - @debug ('or method' $queryString); } } @else { @@ -190,8 +185,6 @@ $mq-em-base: 16px !default; } } - @debug $queryString; - @return $queryString; } } From 0ff736a826239af4faa0b81d84740ac836d90e5b Mon Sep 17 00:00:00 2001 From: Daniel Tonon Date: Thu, 7 Sep 2017 00:13:50 +1000 Subject: [PATCH 05/13] Simplifyling the breakpoint documentation You don't really need to use a function to handle breakpoints. Variables work just fine and make the code base easier to search. So I'm recomending a variable aproach now --- README.md | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 93ef235..9aa1fd5 100644 --- a/README.md +++ b/README.md @@ -466,30 +466,20 @@ $mq-em-base: 10px; //*default: 16px*/ This mixin does not contain any string to pixel value functionality. This is to keep the mixin modular allowing you to use your own code for defining what the breakpoints should be. -It is very easy to create a breakpoint function though. This is what I use in combination with the mq mixin to make writing media queries a breeze. +The easiest way to set up a batch of breakpoints is to save them all as Sass variables, then call on them when using the mixin. `````````scss -$breakPoints: ( - 'minimum': 320px, //*The smallest width that the site is able to shrink to */ - 'tiny': 350px, - 'small': 480px, - 'mobile': 600px, //*!MAJOR BREAK POINT!*//*Maximum for strict mobile view*/ - 'phablet': 770px, //*essentially the maximum for iPads in portrait*/ - 'tablet': 960px, //*!MAJOR BREAK POINT!*/ /*good place to switch to tablet view*/ - 'large': 1024px, //*maximum for iPads in landscape*/ - 'page': 1200px, //*!MAJOR BREAK POINT!*//*Point at which the edge of the desktop design meets the edge of the screen*/ -); - -@function bp($value){ - @return map-get($breakPoints, $value); -} -````````` - -You can then use it in combination with the mq mixin like this: +$BP-minimum: 320px; +$BP-tiny: 350px; +$BP-small: 480px; +$BP-mobile: 600px; +$BP-phablet: 770px; +$BP-tablet: 960px; +$BP-large: 1024px; +$BP-page: 1200px; -````````scss .element { - @include mq(max, bp('mobile')){ + @include mq(max, $BP-mobile){ //styles go here } } From d7a67f75d4484b4bfca8017faa83da56fc163abc Mon Sep 17 00:00:00 2001 From: Daniel Tonon Date: Thu, 7 Sep 2017 00:14:52 +1000 Subject: [PATCH 06/13] Documentation for `plus` keyword (not finished yet) --- README.md | 1304 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 772 insertions(+), 532 deletions(-) diff --git a/README.md b/README.md index 9aa1fd5..4389dfb 100644 --- a/README.md +++ b/README.md @@ -1,474 +1,710 @@ -# mq-scss - -An extremely powerful but easy to use Sass media query mixin - -This media query mixin is a powerful tool that lets you easily create far more complex media queries than you would have ever attempted to do with plain css. It also makes your code far easier to maintain through it's ability to take simple media query variables. - -If you enjoy using mq-scss, try my new [mq-js](https://www.npmjs.com/package/mq-js) npm module. This allows you to use mq-scss style media queries inside your JavaScript files. - -## Contents - -* [Installation](#installation) -* [Basic usage](#basic-usage) - * [Min/Max width](#minmax-width) - * [Inside/Outside](#insideoutside) - * [Changing which value comes first](#changing-which-value-comes-first) - * [Ratio based media queries](#ratio-based-media-queries) - * [Full list of media query ranges](#full-list-of-media-query-ranges) - * [Single value ranges](#single-value-ranges) - * [Double value ranges](#double-value-ranges) -* [MQ variables](#mq-variables) - * [Why name your media queries?](#why-name-your-media-queries) - * [Enhanced maintainability](#enhanced-maintainability) - * [How to use an MQ variable](#how-to-use-an-mq-variable) - * [Naming your MQ variables](#naming-your-mq-variables) - * [Creating your MQ variables](#creating-your-mq-variables) -* [Combining media queries](#combining-media-queries) - * [Media query "or" statements](#media-query-or-statements) - * [Media Query "and" statements](#media-query-and-statements) -* [em conversion](#em-conversion) -* [Defining breakpoints](#defining-breakpoints) -* [Bonus retina display mixin](#bonus-retina-display-mixin) -* [Change Log](#change-log) - -## Installation - -``````````` -npm install mq-scss --save -``````````` - -Import mq-scss at the top of your main Sass file (note that the exact path will differ depending on your folder structure) - -``````scss -@import "../node_modules/mq-scss/mq"; -`````` - -## Basic usage - -``````scss -@include mq($range, $breakpoint-1 [, $breakpoint-2]){ @content } -`````` - -### Min/Max width - -In this example we state that we want the background of the element to be red by default but change to blue if the screen is less than or equal to 600px wide - -`````````````scss -SASS: - -.element { - background: red; - - @include mq(max, 600px){ - background: blue; - } -} -````````````` - -`````````````css -outputted css: - -.element { background: red; } -@media screen and (max-width: 600px) { - .element { background: blue; } -} -````````````` - - -It's just as easy to state a minimum width: - -`````````````scss -SASS: - -.element { - background: red; - - @include mq(min, 600px){ - background: blue; - } -} -````````````` - -`````````````css -outputted css: - -.element { background: red; } -@media screen and (min-width: 601px) { - .element { background: blue; } -} -````````````` - -Note that in the sass, we state that the width is 600px but it gets outputted as 601px in the css. This makes the mixin more intuitive to use and it means you'll never have to worry about that ugly 1px cross over point where `min` and `max` ranges set to the same width display at the same time. - - -### Inside/Outside - -What about those times when you only want a style to be effective within a given range? Perhaps you only want something to be blue if it's a tablet sized screen but not appear on mobile. That is when the `inside` range type comes in handy. - -`````````````scss -SASS: - -.element { - background: red; - - @include mq(inside, 1024px, 600px){ - background: blue; - } -} -````````````` - -`````````````css -outputted css: - -.element { background: red; } -@media screen and (max-width: 1024px) and (min-width: 601px) { - .element { background: blue; } -} -````````````` - -Again notice how min-width gets outputted as +1 the value given to avoid potential conflicts. - -If you want something to be styled a certain way on mobiles and desktops but **not** tablets, we can use the `outside` range type instead: - -`````````````scss -SASS: - -.element { - background: red; - - @include mq(outside, 1024px, 600px){ - background: blue; - } -} -````````````` - -`````````````css -outputted css: - -.element { background: red; } - -@media screen and (max-width: 600px), screen and (min-width: 1025px) { - .element { background: blue; } -} -````````````` - -#### Changing which value comes first - -As of version 1.2.0, you no longer need to worry about which value you use first. Place the breakpoint values in any order and the mixin will figure it out from there. - -Prior to v1.2.0 You needed to set an `$mq-largest-first` global setting variable to `false` if you wanted to place the smaller breakpoint before the larger breakpoint. That is no longer necessary. - - -### Ratio based media queries - -Ratio ranges must be a division in the form of a sting like `'2 / 1'` (width / height). That example meaning that the screen width is 2 times the size of the screen height. Ratio ranges do not accept pixel values. - -`````````````scss -SASS: - -.element { - background: red; - - @include mq(min-ratio, '2 / 1'){ - background: blue; - } -} -````````````` - -`````````````css -outputted css: - -.element { background: red; } -@media screen and (min-aspect-ratio: 2 / 1) { - .element { background: blue; } -} -````````````` - -It is easiest to think of ratio based media queries as always being based more on the width than the height. For example, `mq('min-ratio', '2 / 1')` will be active at a ratio of `3 / 1` but not at a ratio of `1 / 1` or `2 / 3`. `min-ratio` acts a little bit like `min-width` in this sense. Since `3 / 1` is wider than `2 / 1` it will be active but both `1 / 1` and `2 / 3` are thinner than `2 / 1` so those are not active. - -There are 2 types of ratio based media queries, "aspect-ratio" (shortened to just `ratio` in the mq mixin) and "device-aspect-ratio" (shortened to `device-ratio` in the mq mixin). It is generally best to stick with "aspect-ratio" rather than "device-aspect-ratio" since "aspect-ratio" is determined by the users browser window size. "device-aspect-ratio" is based on the physical screen size of the users device. With "aspect-ratio" you can see the effects by just resizing the browser window. With "device-aspect-ratio" you will physically have to look at the site on a different screen in order to see the effect... or look at the site with the Chrome dev tools screen emulator open (Chromes screen emulator obeys "device-aspect-ratio" media queries). - -Ratio based media queries are mostly useful for when you have sites that have displays that take up the full screen. Displays like this tend to need media queries that understand both the users screen height and width at the same time. You may need to combine the ratio based media query with a more traditional pixel based media query for it to have the greatest effect. Read the [Media Query "and" statements](#media-query-and-statements) section for more details on how to do that. - -### Full list of media query ranges - - @include mq([range], XXX, YYY){ /*styles*/ } - -These examples are based on XXX being a larger value than YYY. - -Note that orientation and ratio ranges do **not** accept pixel values. - -Also, `orientation` only accepts the strings `'portrait'` and `'landscape'`. - -#### Single value ranges - -- **min** : `screen and (min-width: XXX)` -- **max** : `screen and (max-width: XXX)` - -- **min-height** : `screen and (min-height: XXX)` -- **max-height** : `screen and (max-height: XXX)` - -- **ratio** : `screen and (aspect-ratio: XXX)` -- **min-ratio** : `screen and (min-aspect-ratio: XXX)` -- **max-ratio** : `screen and (max-aspect-ratio: XXX)` - -- **device-ratio** : `screen and (device-aspect-ratio: XXX)` -- **min-device-ratio** : `screen and (min-device-aspect-ratio: XXX)` -- **max-device-ratio** : `screen and (max-device-aspect-ratio: XXX)` - -- **orientation** : `screen and (orientation: XXX)` - -#### Double value ranges - -- **inside** : `screen and (max-width: XXX) and (min-width: YYY)` -- **outside** : `screen and (max-width: YYY), screen and (min-width: XXX)` - -- **inside-height** : `screen and (max-height: XXX) and (min-height: YYY)` -- **outside-height** : `screen and (max-height: YYY), screen and (min-height: XXX)` - -- **inside-ratio** : `screen and (max-aspect-ratio: XXX) and (min-aspect-ratio: YYY)` -- **outside-ratio** : `screen and (max-aspect-ratio: YYY), screen and (min-aspect-ratio: XXX)` - -- **inside-device-ratio** : `screen and (max-device-aspect-ratio: XXX) and (min-device-aspect-ratio: YYY)` -- **outside-device-ratio** : `screen and (max-device-aspect-ratio: YYY), screen and (min-device-aspect-ratio: XXX)` - - -## MQ variables - -There are two very strong reasons for using an MQ variable over writing the media query inline like I've been doing so far. - -### Why name your media queries? - -Have you ever opened up a style sheet, seen a wall of media queries and not have a clue what any of them are for? Ok you probably understood that a certain block of styles was to make the site look good on mobile, but I'm not talking about that. I mean just from looking at the code, were you able to understand _why_ the developer wrote those styles? - -Styles in Media queries are always written with a certain objective in mind. Objectives like making the sidebar full width or making the text smaller. Most of the time, these individual objectives are all lumped under the same media query. To make things even more confusing, each of those little objectives may need to affect multiple elements to achieve the desired result. For example, making the sidebar full width might mean having to making other elements on the page wider as well. - -What if you decide later on that you actually want the sidebar to go full width at a larger screen size? You don't want the text shrinking at that larger screen size though, that's good as it is. Well generally you would need to create a new media query and then scour the styles looking for the side bar related stuff, then cherry pick that stuff out. Often that leads to styles being missed and then you being left all dazed and confused about why your site is looking broken. Wouldn't it be easier if the styles were already broken down into the individual objectives that the styling was trying to achieve in the first place? - -### Enhanced maintainability - -You state the media query once at the top of your Sass file and then you can re-use it as many times as you like. If you need to change it later on, you change it once and it updates across the entire style sheet. In combination with the ability to name the variables based on the objective that they are trying to achieve, MQ variables make working with media queries far easier to maintain. - -### How to use an MQ variable - -#### Naming your MQ variables - -I've come up with a bit of a naming convention for them based on BEM. If you don't agree with the naming convention, feel free not to use it. It's just a variable name and has no effect on the mixin itself. - -This is how I write an Media Query variable: - -`````````````scss -$MQ-[element]__[property]--[state]: ([range], [breakpoint-1], [breakpoint-2]); -````````````` - -Here is the breakdown of what each part means. I tend to use camelCase for each group to keep the grouping clear. - -**$MQ** - MQ at the start tells us that it's a media query variable (helps when scanning through the code) - -**[element]** - The target element name. So for `.car__door` [element] would be `door`. - -**[property]** - This one is optional. It represents the main css property that you are catering for in the media query. - -**[state]** - A name for the state that the element is in when the media query is true. Try to keep it as short as possible but still clear. - -**([range], [breakpoint-1], [breakpoint-2])** - the exact same as what you would put between the brackets of the media query mixin if you were doing it inline. - -#### Creating your MQ variables - -Here is an example of how to use it: - -`````````````scss -SASS: - -$MQ-element__color--main: (inside, 1024px, 600px); -$MQ-element__color--alt: (outside, 1024px, 600px); - -.module { - &__element { - @include mq($MQ-element__color--main){ - background: red; - } - - @include mq($MQ-element__color--alt){ - background: blue; - } - - &--green { - @include mq($MQ-element__color--main){ - background: green; - } - - @include mq($MQ-element__color--alt){ - background: grey; - } - } - } -} -````````````` - -`````````````css -outputted css: - -@media not screen and (max-width: 1024px) and (min-width: 601px) { - .module__element { background: red; } -} -@media screen and (max-width: 1024px) and (min-width: 601px) { - .module__element { background: blue; } -} -@media not screen and (max-width: 1024px) and (min-width: 601px) { - .module__element--green { background: green; } -} -@media screen and (max-width: 1024px) and (min-width: 601px) { - .module__element--green { background: grey; } -} -````````````` - -Ahhhhh!!! It's doubling up on Media queries!!! Think of all that extra weight you're adding!!! - -Well actually after gzipping, all the repetitive media query declarations [become quite negligible](https://benfrain.com/inline-or-combined-media-queries-in-sass-fight/). - -## Combining media queries - -### Media query "or" statements - -Media Query "or" statements are only possible using an MQ variable. - -`````````````scss -SASS: - -$MQ-element__color--alt: - (inside, 1024px, 980px), - (max, 600px) -; - -.element { - background: red; - - @include mq($MQ-element__color--alt){ - background: blue; - } -} -````````````` - -`````````````css -outputted css: - -.element { background: red; } -@media screen and (max-width: 1024px) and (min-width: 981px), screen and (max-width: 600px) { - .element { background: blue; } -} -````````````` - -This technique is most useful when you are targeting a module that is inside a container that is changing in width quite frequently. It's a bit harder to make a counter media query for these though since as long as just a single rule in the or statement is true, the styles will take effect. To effectively create a counter media query for one of these multi queries, you need to carefully target all the gaps in the original statement. - -`````````````scss -SASS: - -$MQ-element__color--main: - (inside, 1024px, 980px), - (max, 600px) -; - -$MQ-element__color--alt: - (min, 1024px),//*$MQ-element__color--main does not go any higher than 1024px*/ - (inside, 980px, 600px)//*$MQ-element__color--main does not target screen sizes between 980px and 600px.*/ - //*$MQ-element__color--main covers all screen sizes below 600px so no further queries are needed for the counter query*/ -; - -.element { - @include mq($MQ-element__color--main){ - background: red; - } - @include mq($MQ-element__color--alt){ - background: blue; - } -} -````````````` - -`````````````css -outputted css: - -@media screen and (max-width: 1024px) and (min-width: 981px), screen and (max-width: 600px) { - .element { background: blue; } -} -@media screen and (min-width: 1025px), screen and (max-width: 980px) and (min-width: 601px) { - .element { background: red; } -} -````````````` - -### Media Query "and" statements - -So the scenario is that you have some styles you want to apply only when both the side bar is full width and the sub heading is hidden. This is the easiest way to do that: - -`````````````scss -$MQ-sideBar__width--full: (max, 600px); -$MQ-subHeading--hidden: (inside, 800px, 400px); - -.module { - &__sideBar { - width: 33.33%; - @include mq($MQ-sideBar__width--full){ - width: 100%; - } - } - &__subHeading { - @include mq($MQ-subHeading--hidden){ - display: none; - } - } - &__mainHeading { - @include mq($MQ-sideBar__width--full){ - @include mq($MQ-subHeading--hidden){ - //Styles that only apply when both the sidebar is full width and the subheading is hidden - background: red; - } - } - } -} -````````````` - -`````````````css -outputted css: - -.module__sideBar { width: 33.33%; } -@media screen and (max-width: 600px) { - .module__sideBar { width: 100%; } -} -@media screen and (max-width: 800px) and (min-width: 401px) { - .module__subHeading { display: none; } -} -@media screen and (max-width: 600px) and (max-width: 800px) and (min-width: 401px) { - .module__mainHeading { background: red; } -} -````````````` - -I'm looking into a more streamlined way of incorporating media query "and" statements without having to nest them inside one another like this but currently this is the best available method. - -As of version 1.2.0 the `outside` range types also support this feature. - -## em conversion - -Pixel based media queries can actually appear incorrectly when zooming on some browsers (it's particularly infamous in Safari on Mac). - -There are 2 setting variables used to control the em conversion functionality. These settings are defined before the import statement. - -````````````scss -$mq-ems: true; //*default: false*/ -$mq-em-base: 10px; //*default: 16px*/ -@import '../node_modules/mq-scss/mq'; -```````````` - -**$mq-ems** defines if the media query mixin should bother doing conversions or not. - -**$mq-em-base** defines the base value that the media query uses for doing it's em conversion calculations. - -## Defining breakpoints - -This mixin does not contain any string to pixel value functionality. This is to keep the mixin modular allowing you to use your own code for defining what the breakpoints should be. - +# mq-scss + +An extremely powerful but easy to use Sass media query mixin + +This media query mixin is a powerful tool that lets you easily create far more complex media queries than you would have ever attempted to do with plain css. It also makes your code far easier to maintain through it's ability to take simple media query variables. + +If you enjoy using mq-scss, try my new [mq-js](https://www.npmjs.com/package/mq-js) npm module. This allows you to use mq-scss style media queries inside your JavaScript files. + +## Contents + +* [Installation](#installation) +* [Basic usage](#basic-usage) + * [Min/Max width](#minmax-width) + * [Inside/Outside](#insideoutside) + * [Changing which value comes first](#changing-which-value-comes-first) + * [Ratio based media queries](#ratio-based-media-queries) + * [Full list of media query ranges](#full-list-of-media-query-ranges) + * [Single value ranges](#single-value-ranges) + * [Double value ranges](#double-value-ranges) +* [MQ variables](#mq-variables) + * [Why name your media queries?](#why-name-your-media-queries) + * [Enhanced maintainability](#enhanced-maintainability) + * [How to use an MQ variable](#how-to-use-an-mq-variable) + * [Naming your MQ variables](#naming-your-mq-variables) + * [Creating your MQ variables](#creating-your-mq-variables) +* [Combining media queries](#combining-media-queries) + * [Media query "or" statements](#media-query-or-statements) + * [Media Query "and" statements](#media-query-and-statements) +* [em conversion](#em-conversion) +* [Defining breakpoints](#defining-breakpoints) +* [Bonus retina display mixin](#bonus-retina-display-mixin) +* [Change Log](#change-log) + +## Installation + +``````````` +npm install mq-scss --save +``````````` + +Import mq-scss at the top of your main Sass file (note that the exact path will differ depending on your folder structure) + +``````scss +@import "../node_modules/mq-scss/mq"; +`````` + +## Basic usage + +``````scss +@include mq($range, $breakpoint-1 [, $breakpoint-2]){ @content } +`````` + +### Min/Max width + +In this example we state that we want the background of the element to be red by default but change to blue if the screen is less than or equal to 600px wide + +`````````````scss +SASS: + +.element { + background: red; + + @include mq(max, 600px){ + background: blue; + } +} +````````````` + +`````````````css +/* outputted css: */ + +.element { background: red; } +@media screen and (max-width: 600px) { + .element { background: blue; } +} +````````````` + + +It's just as easy to state a minimum width: + +`````````````scss +SASS: + +.element { + background: red; + + @include mq(min, 600px){ + background: blue; + } +} +````````````` + +`````````````css +/* outputted css: */ + +.element { background: red; } +@media screen and (min-width: 601px) { + .element { background: blue; } +} +````````````` + +Note that in the sass, we state that the width is 600px but it gets outputted as 601px in the css. This makes the mixin more intuitive to use and it means you'll never have to worry about that ugly 1px cross over point where `min` and `max` ranges set to the same width display at the same time. + + +### Inside/Outside + +What about those times when you only want a style to be effective within a given range? Perhaps you only want something to be blue if it's a tablet sized screen but not appear on mobile. That is when the `inside` range type comes in handy. + +`````````````scss +SASS: + +.element { + background: red; + + @include mq(inside, 1024px, 600px){ + background: blue; + } +} +````````````` + +`````````````css +/* outputted css: */ + +.element { background: red; } +@media screen and (max-width: 1024px) and (min-width: 601px) { + .element { background: blue; } +} +````````````` + +Again notice how min-width gets outputted as +1 the value given to avoid potential conflicts. + +If you want something to be styled a certain way on mobiles and desktops but **not** tablets, we can use the `outside` range type instead: + +`````````````scss +SASS: + +.element { + background: red; + + @include mq(outside, 1024px, 600px){ + background: blue; + } +} +````````````` + +`````````````css +/* outputted css: */ + +.element { background: red; } + +@media screen and (max-width: 600px), screen and (min-width: 1025px) { + .element { background: blue; } +} +````````````` + +#### Changing which value comes first + +As of version 1.2.0, you no longer need to worry about which value you use first. Place the breakpoint values in any order and the mixin will figure it out from there. + +Prior to v1.2.0 You needed to set an `$mq-largest-first` global setting variable to `false` if you wanted to place the smaller breakpoint before the larger breakpoint. That is no longer necessary. + + +### Ratio based media queries + +Ratio ranges must be a division in the form of a sting like `'2 / 1'` (width / height). That example meaning that the screen width is 2 times the size of the screen height. Ratio ranges do not accept pixel values. + +`````````````scss +SASS: + +.element { + background: red; + + @include mq(min-ratio, '2 / 1'){ + background: blue; + } +} +````````````` + +`````````````css +/* outputted css: */ + +.element { background: red; } +@media screen and (min-aspect-ratio: 2 / 1) { + .element { background: blue; } +} +````````````` + +It is easiest to think of ratio based media queries as always being based more on the width than the height. For example, `mq('min-ratio', '2 / 1')` will be active at a ratio of `3 / 1` but not at a ratio of `1 / 1` or `2 / 3`. `min-ratio` acts a little bit like `min-width` in this sense. Since `3 / 1` is wider than `2 / 1` it will be active but both `1 / 1` and `2 / 3` are thinner than `2 / 1` so those are not active. + +There are 2 types of ratio based media queries, "aspect-ratio" (shortened to just `ratio` in the mq mixin) and "device-aspect-ratio" (shortened to `device-ratio` in the mq mixin). It is generally best to stick with "aspect-ratio" rather than "device-aspect-ratio" since "aspect-ratio" is determined by the users browser window size. "device-aspect-ratio" is based on the physical screen size of the users device. With "aspect-ratio" you can see the effects by just resizing the browser window. With "device-aspect-ratio" you will physically have to look at the site on a different screen in order to see the effect... or look at the site with the Chrome dev tools screen emulator open (Chromes screen emulator obeys "device-aspect-ratio" media queries). + +Ratio based media queries are mostly useful for when you have sites that have displays that take up the full screen. Displays like this tend to need media queries that understand both the users screen height and width at the same time. You may need to combine the ratio based media query with a more traditional pixel based media query for it to have the greatest effect. Read the [Media Query "and" statements](#media-query-and-statements) section for more details on how to do that. + +### Full list of media query ranges + + @include mq([range], XXX, YYY){ /*styles*/ } + +These examples are based on XXX being a larger value than YYY. + +Note that orientation and ratio ranges do **not** accept pixel values. + +Also, `orientation` only accepts the strings `'portrait'` and `'landscape'`. + +#### Single value ranges + +- **min** : `screen and (min-width: XXX)` +- **max** : `screen and (max-width: XXX)` + +- **min-height** : `screen and (min-height: XXX)` +- **max-height** : `screen and (max-height: XXX)` + +- **ratio** : `screen and (aspect-ratio: XXX)` +- **min-ratio** : `screen and (min-aspect-ratio: XXX)` +- **max-ratio** : `screen and (max-aspect-ratio: XXX)` + +- **device-ratio** : `screen and (device-aspect-ratio: XXX)` +- **min-device-ratio** : `screen and (min-device-aspect-ratio: XXX)` +- **max-device-ratio** : `screen and (max-device-aspect-ratio: XXX)` + +- **orientation** : `screen and (orientation: XXX)` + +#### Double value ranges + +- **inside** : `screen and (max-width: XXX) and (min-width: YYY)` +- **outside** : `screen and (max-width: YYY), screen and (min-width: XXX)` + +- **inside-height** : `screen and (max-height: XXX) and (min-height: YYY)` +- **outside-height** : `screen and (max-height: YYY), screen and (min-height: XXX)` + +- **inside-ratio** : `screen and (max-aspect-ratio: XXX) and (min-aspect-ratio: YYY)` +- **outside-ratio** : `screen and (max-aspect-ratio: YYY), screen and (min-aspect-ratio: XXX)` + +- **inside-device-ratio** : `screen and (max-device-aspect-ratio: XXX) and (min-device-aspect-ratio: YYY)` +- **outside-device-ratio** : `screen and (max-device-aspect-ratio: YYY), screen and (min-device-aspect-ratio: XXX)` + + +## MQ variables + +There are two very strong reasons for using an MQ variable over writing the media query inline like I've been doing so far. + +### Why name your media queries? + +Have you ever opened up a style sheet, seen a wall of media queries and not have a clue what any of them are for? Ok you probably understood that a certain block of styles was to make the site look good on mobile, but I'm not talking about that. I mean just from looking at the code, were you able to understand _why_ the developer wrote those styles? + +Styles in Media queries are always written with a certain objective in mind. Objectives like making the sidebar full width or making the text smaller. Most of the time, these individual objectives are all lumped under the same media query. To make things even more confusing, each of those little objectives may need to affect multiple elements to achieve the desired result. For example, making the sidebar full width might mean having to making other elements on the page wider as well. + +What if you decide later on that you actually want the sidebar to go full width at a larger screen size? You don't want the text shrinking at that larger screen size though, that's good as it is. Well generally you would need to create a new media query and then scour the styles looking for the side bar related stuff, then cherry pick that stuff out. Often that leads to styles being missed and then you being left all dazed and confused about why your site is looking broken. Wouldn't it be easier if the styles were already broken down into the individual objectives that the styling was trying to achieve in the first place? + +### Enhanced maintainability + +You state the media query once at the top of your Sass file and then you can re-use it as many times as you like. If you need to change it later on, you change it once and it updates across the entire style sheet. In combination with the ability to name the variables based on the objective that they are trying to achieve, MQ variables make working with media queries far easier to maintain. + +### How to use an MQ variable + +#### Naming your MQ variables + +I've come up with a bit of a naming convention for them based on BEM. If you don't agree with the naming convention, feel free not to use it. It's just a variable name and has no effect on the mixin itself. + +This is how I write an Media Query variable: + +`````````````scss +$MQ-[element]__[property]--[state]: ([range], [breakpoint-1], [breakpoint-2]); +````````````` + +Here is the breakdown of what each part means. I tend to use camelCase for each group to keep the grouping clear. + +**$MQ** - MQ at the start tells us that it's a media query variable (helps when scanning through the code) + +**[element]** - The target element name. So for `.car__door` [element] would be `door`. + +**[property]** - This one is optional. It represents the main css property that you are catering for in the media query. + +**[state]** - A name for the state that the element is in when the media query is true. Try to keep it as short as possible but still clear. + +**([range], [breakpoint-1], [breakpoint-2])** - the exact same as what you would put between the brackets of the media query mixin if you were doing it inline. + +#### Creating your MQ variables + +Here is an example of how to use it: + +`````````````scss +SASS: + +$MQ-element__color--main: (inside, 1024px, 600px); +$MQ-element__color--alt: (outside, 1024px, 600px); + +.module { + &__element { + @include mq($MQ-element__color--main){ + background: red; + } + + @include mq($MQ-element__color--alt){ + background: blue; + } + + &--green { + @include mq($MQ-element__color--main){ + background: green; + } + + @include mq($MQ-element__color--alt){ + background: grey; + } + } + } +} +````````````` + +`````````````css +/* outputted css: */ + +@media not screen and (max-width: 1024px) and (min-width: 601px) { + .module__element { background: red; } +} +@media screen and (max-width: 1024px) and (min-width: 601px) { + .module__element { background: blue; } +} +@media not screen and (max-width: 1024px) and (min-width: 601px) { + .module__element--green { background: green; } +} +@media screen and (max-width: 1024px) and (min-width: 601px) { + .module__element--green { background: grey; } +} +````````````` + +Ahhhhh!!! It's doubling up on Media queries!!! Think of all that extra weight you're adding!!! + +Well actually after gzipping, all the repetitive media query declarations [become quite negligible](https://benfrain.com/inline-or-combined-media-queries-in-sass-fight/). + +## Combining media queries + +### Media query "or" statements + +Media Query "or" statements are only possible using an MQ variable. + +`````````````scss +SASS: + +$MQ-element__color--alt: + (inside, 1024px, 980px), + (max, 600px) +; + +.element { + background: red; + + @include mq($MQ-element__color--alt){ + background: blue; + } +} +````````````` + +`````````````css +/* outputted css: */ + +.element { background: red; } +@media screen and (max-width: 1024px) and (min-width: 981px), screen and (max-width: 600px) { + .element { background: blue; } +} +````````````` + +This technique is most useful when you are targeting a module that is inside a container that is changing in width quite frequently. It's a bit harder to make a counter media query for these though since as long as just a single rule in the or statement is true, the styles will take effect. To effectively create a counter media query for one of these multi queries, you need to carefully target all the gaps in the original statement. + +`````````````scss +SASS: + +$MQ-element__color--main: + (inside, 1024px, 980px), + (max, 600px) +; + +$MQ-element__color--alt: + (min, 1024px),//*$MQ-element__color--main does not go any higher than 1024px*/ + (inside, 980px, 600px)//*$MQ-element__color--main does not target screen sizes between 980px and 600px.*/ + //*$MQ-element__color--main covers all screen sizes below 600px so no further queries are needed for the counter query*/ +; + +.element { + @include mq($MQ-element__color--main){ + background: red; + } + @include mq($MQ-element__color--alt){ + background: blue; + } +} +````````````` + +`````````````css +/* outputted css: */ + +@media screen and (max-width: 1024px) and (min-width: 981px), screen and (max-width: 600px) { + .element { background: blue; } +} +@media screen and (min-width: 1025px), screen and (max-width: 980px) and (min-width: 601px) { + .element { background: red; } +} +````````````` + +### Media Query "and" statements + +So the scenario is that you have some styles you want to apply only when both the side bar is full width and the sub heading is hidden. This is the easiest way to do that: + +`````````````scss +$MQ-sideBar__width--full: (max, 600px); +$MQ-subHeading--hidden: (inside, 800px, 400px); +$MQ-mainHeading--red: ($MQ-sideBar__width--full plus $MQ-subHeading--hidden); + +.module { + &__sideBar { + width: 33.33%; + @include mq($MQ-sideBar__width--full){ + width: 100%; + } + } + &__subHeading { + @include mq($MQ-subHeading--hidden){ + display: none; + } + } + &__mainHeading { + @include mq($MQ-mainHeading--red){ + //Styles that only apply when both the sidebar is full width and the subheading is hidden + background: red; + } + } +} +````````````` + +`````````````css +/* outputted css: */ + +.module__sideBar { width: 33.33%; } +@media screen and (max-width: 600px) { + .module__sideBar { width: 100%; } +} +@media screen and (max-width: 800px) and (min-width: 401px) { + .module__subHeading { display: none; } +} +@media screen and (max-width: 600px) and (max-width: 800px) and (min-width: 401px) { + .module__mainHeading { background: red; } +} +````````````` + +This technique utilises the `plus` keyword (introduced in version 1.3.0) to glue the two media queries together into a single, more easily transportable, combined MQ variable. + +This can also be done inline as a one off like this: + +`````````````scss +$MQ-sideBar__width--full: (max, 600px); +$MQ-subHeading--hidden: (inside, 800px, 400px); + +.module { + &__sideBar { + width: 33.33%; + @include mq($MQ-sideBar__width--full){ + width: 100%; + } + } + &__subHeading { + @include mq($MQ-subHeading--hidden){ + display: none; + } + } + &__mainHeading { + @include mq($MQ-sideBar__width--full plus $MQ-subHeading--hidden){ + //Styles that only apply when both the sidebar is full width and the subheading is hidden + background: red; + } + } +} +````````````` + +It will even work as part of an "or" statement: + +`````````````scss +$MQ-subHeading--hidden: (inside, 800px, 400px); +$MQ-sideBar__width--full: (max, 600px); + +$MQ-mainHeading--red: ( + (min-ratio, '2 / 1') plus $MQ-subHeading--hidden, + $MQ-sideBar__width--full +); + +.module { + &__sideBar { + width: 33.33%; + @include mq($MQ-sideBar__width--full){ + width: 100%; + } + } + &__subHeading { + @include mq($MQ-subHeading--hidden){ + display: none; + } + } + &__mainHeading { + @include mq($MQ-mainHeading--red){ + background: red; + } + } +} +````````````` + +`````````````css +/* outputted css: */ + +.module__sideBar { + width: 33.33%; +} +@media screen and (max-width: 600px) { + .module__sideBar { + width: 100%; + } +} +@media screen and (max-width: 800px) and (min-width: 401px) { + .module__subHeading { + display: none; + } +} +@media screen and (min-aspect-ratio: 2 / 1) and (max-width: 800px) and (min-width: 401px), screen and (max-width: 600px) { + .module__mainHeading { + background: red; + } +} +````````````` + +You can also string multiple `plus` statements together: + +`````````````scss +$MQ-test1: (inside, 800px, 400px); +$MQ-test2: (max, 600px); + +$MQ-test3: ($MQ-test1 plus $MQ-test2 plus (min-ratio, '2 / 1')); + +.module { + @include mq($MQ-test3){ + background: red; + } +} +````````````` + +`````````````css +/* outputted css: */ +@media screen and (max-width: 800px) and (min-width: 401px) and (max-width: 600px) and (min-aspect-ratio: 2 / 1) { + .module { + background: red; + } +} +````````````` + +### !IMPORTANT! `plus` does not work in all situations + +There are some restrictions around using the `plus` keyword that you should be aware of. I haven't been able to write error messages for these edge cases warning users not to do these things. I didn't want the lack of error messages to hold back the feature though. + +#### It can not be used in conjunction with any `outside` range type + +The following code **will not work** (at least not as expected) + +`````````````scss +$MQ-test1: (outside, 800px, 400px); +$MQ-test2: (max, 600px); + +$MQ-test3: ($MQ-test1 plus $MQ-test2); + +.module { + @include mq($MQ-test3){ + background: red; + } +} +````````````` +`````````````css +/* outputted css: */ +/* (max-width: 400px) is on it's own, not affected by the (max-width: 600px) rule */ +@media screen and (max-width: 400px), screen and (min-width: 801px) and (max-width: 600px) { + .module { + background: red; + } +} +````````````` + +This issue occurs across all `outside` range types (`outside`, `outside-height`, `outside-ratio`, `outside-device-ratio`). + +#### `plus` statements can not contain any `or` statements + +`Or` statements can contain `plus` statements however `plus` statements can not contain `or` statements. + +The following code **will not work** (at least not as expected) + +`````````````scss +$MQ-test1: ( + (inside, 1200px, 800px), + (max, 400px) +); +$MQ-test2: (max, 600px); + +$MQ-test3: ($MQ-test1 plus $MQ-test2); + +.module { + @include mq($MQ-test3){ + background: red; + } +} +````````````` +`````````````css +/* outputted css: */ +/* (max-width: 600px) rule is completely ignored */ +@media screen and (max-width: 1200px) and (min-width: 801px) { + .module { + background: red; + } +} +````````````` + +#### Work arounds + +You can generally get around these issues by placing the `plus` statement inside `or` statements. + +So instead of this: + +`````````````scss +$MQ-test1: (outside, 800px, 400px); +$MQ-test2: (max, 600px); + +$MQ-test3: ($MQ-test1 plus $MQ-test2); + +.module { + @include mq($MQ-test3){ + background: red; + } +} +````````````` + +Do this: + +`````````````scss +$MQ-test1: (outside, 800px, 400px); +$MQ-test2: (max, 600px); + +/////////////////////////////////////////////////// Test is not working + +$MQ-test3: ( + ((min, 800px) plus $MQ-test2), + ((max, 400px) plus $MQ-test2) +); + +.module { + @include mq($MQ-test3){ + background: red; + } +} +````````````` +`````````````css +/* Test is not working, should be outputting something different */ + +@media screen and (min-width: 801px) and (max-width: 600px) { + .module { + background: red; + } +} +````````````` + +Also, as of version 1.2.0, you can utilise the native media query merging feature in Sass to sort out the media queries for you (as long as you don't mind them being outside of variables) + +`````````````scss +$MQ-test1: (outside, 800px, 400px); +$MQ-test2: (max, 600px); + +.module { + @include mq($MQ-test1){ + @include mq($MQ-test2){ + background: red; + } + } +} +````````````` +`````````````css +/* outputted css */ +@media screen and (max-width: 400px) and (max-width: 600px), screen and (min-width: 801px) and (max-width: 600px) { + .module { + background: red; + } +} +````````````` + +## em conversion + +Pixel based media queries can actually appear incorrectly when zooming on some browsers (it's particularly infamous in Safari on Mac). + +There are 2 setting variables used to control the em conversion functionality. These settings are defined before the import statement. + +````````````scss +$mq-ems: true; //*default: false*/ +$mq-em-base: 10px; //*default: 16px*/ +@import '../node_modules/mq-scss/mq'; +```````````` + +**$mq-ems** defines if the media query mixin should bother doing conversions or not. + +**$mq-em-base** defines the base value that the media query uses for doing it's em conversion calculations. + +## Defining breakpoints + +This mixin does not contain any string to pixel value functionality. This is to keep the mixin modular allowing you to use your own code for defining what the breakpoints should be. + The easiest way to set up a batch of breakpoints is to save them all as Sass variables, then call on them when using the mixin. - -`````````scss + +`````````scss $BP-minimum: 320px; $BP-tiny: 350px; $BP-small: 480px; @@ -477,66 +713,70 @@ $BP-phablet: 770px; $BP-tablet: 960px; $BP-large: 1024px; $BP-page: 1200px; - -.element { + +.element { @include mq(max, $BP-mobile){ - //styles go here - } -} -```````` - -## Bonus retina display mixin - -I've also added a retina display mixin for detecting retina display devices - -````````scss -@include retina($density: 2) { @content; } -```````` - -It can be used like this: - -````````scss -.element { - @include retina { - /* styles that will only appear on retina screen devices (minimum of 2dppx) */ - } - @include retina(3) { - //styles that will only appear on retina screen devices that are a minimum of 3dppx - } -} -```````` - -To create this css: - -````````scss -@media only screen and (min-device-pixel-ratio: 2), - only screen and (min-resolution: 192ppi), - only screen and (min-resolution: 2dppx) { - - .element { - /* styles that will only appear on retina screen devices (minimum of 2dppx) */ - } -} - -@media only screen and (min-device-pixel-ratio: 3), - only screen and (min-resolution: 288ppi), - only screen and (min-resolution: 3dppx) { - - .element { - /* styles that will only appear on retina screen devices that are a minimum of 3dppx */ - } -} -```````` - - -## Change log - -### v1.2.0 - -- Removed the need for the `$mq-largest-first` variable. You can now state double value breakpoint values in any order. -- Outside range types can now be safely nested and take advantage of the Sass nested media queries functionality. -- Updated the MQ variable syntax to what I currently use. - -### v1.1.0 - -- Added the ability to state the smaller value first by setting an `$mq-largest-first` variable to `false`. + //styles go here + } +} +```````` + +## Bonus retina display mixin + +I've also added a retina display mixin for detecting retina display devices + +````````scss +@include retina($density: 2) { @content; } +```````` + +It can be used like this: + +````````scss +.element { + @include retina { + /* styles that will only appear on retina screen devices (minimum of 2dppx) */ + } + @include retina(3) { + //styles that will only appear on retina screen devices that are a minimum of 3dppx + } +} +```````` + +To create this css: + +````````scss +@media only screen and (min-device-pixel-ratio: 2), + only screen and (min-resolution: 192ppi), + only screen and (min-resolution: 2dppx) { + + .element { + /* styles that will only appear on retina screen devices (minimum of 2dppx) */ + } +} + +@media only screen and (min-device-pixel-ratio: 3), + only screen and (min-resolution: 288ppi), + only screen and (min-resolution: 3dppx) { + + .element { + /* styles that will only appear on retina screen devices that are a minimum of 3dppx */ + } +} +```````` + + +## Change log + +### v1.3.0 + +- Added the `plus` keyword for improved handling of "and" statements + +### v1.2.0 + +- Removed the need for the `$mq-largest-first` variable. You can now state double value breakpoint values in any order. +- Outside range types can now be safely nested and take advantage of the Sass nested media queries functionality. +- Updated the MQ variable syntax to what I currently use. + +### v1.1.0 + +- Added the ability to state the smaller value first by setting an `$mq-largest-first` variable to `false`. From ff7e3a75e1f2a627b466e77d6d7054c44da444ff Mon Sep 17 00:00:00 2001 From: Daniel Tonon Date: Thu, 7 Sep 2017 23:31:42 +1000 Subject: [PATCH 07/13] Fixed "or" statement bug FYI: this is now a valid Media Query variable: $MQ-test: ( ((min, 800px) plus (inside, 700px, 500px) plus (min-ratio, '1 / 2')), ((min, 400px) plus (max, 500px)) ); --- _mq.scss | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/_mq.scss b/_mq.scss index db42b91..1c16274 100644 --- a/_mq.scss +++ b/_mq.scss @@ -133,7 +133,7 @@ $mq-em-base: 16px !default; @return calculateMQ($newRange, $newWidth1, $newWidth2); } -@function joinQueries($range){ +@function joinQueries($range, $queryString: ()){ $isAndStatment: false; @@ -160,8 +160,6 @@ $mq-em-base: 16px !default; //@include mq($MQ-example--multiple){} } @else { - $queryString: (); - @each $mqSet in $range { @if ($mqSet != 'plus') { @@ -180,7 +178,11 @@ $mq-em-base: 16px !default; } } @else { - $queryString: $queryString + joinQueries($mqSet); + @if ($isAndStatment) { + $queryString: joinQueries($mqSet, $queryString); + } @else { + $queryString: append($queryString, joinQueries($mqSet), 'comma'); + } } } } From ac046ea589e22e138e46537a93cfea26c1ac2503 Mon Sep 17 00:00:00 2001 From: Daniel Tonon Date: Fri, 8 Sep 2017 00:04:57 +1000 Subject: [PATCH 08/13] mq-scss throws an error if it detects an "or" statement inside a "plus" statement --- _mq.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/_mq.scss b/_mq.scss index 1c16274..aad7021 100644 --- a/_mq.scss +++ b/_mq.scss @@ -180,6 +180,10 @@ $mq-em-base: 16px !default; } @else { @if ($isAndStatment) { $queryString: joinQueries($mqSet, $queryString); + @if (length($queryString) > 1){ + $error: 'mq-scss does not support "or" statements inside "plus" statements: ' + $queryString; + @error $error; + } } @else { $queryString: append($queryString, joinQueries($mqSet), 'comma'); } From 187efcfb8ffe45f2612dbfe36e15ffdcf5a5ae5d Mon Sep 17 00:00:00 2001 From: Daniel Tonon Date: Fri, 8 Sep 2017 00:32:25 +1000 Subject: [PATCH 09/13] Added an error message for when "outside" is used in a "plus" statement --- _mq.scss | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/_mq.scss b/_mq.scss index aad7021..b5f95d2 100644 --- a/_mq.scss +++ b/_mq.scss @@ -122,24 +122,29 @@ $mq-em-base: 16px !default; @return type-of(nth($range,1)) == string; } -@function calculateBasicQuery($mqSet){ - $newRange: nth($mqSet,1); - $newWidth1: nth($mqSet,2); - $newWidth2: null; +@function calculateBasicQuery($mqSet, $isAndStatement: false){ + $range: nth($mqSet,1); + $width1: nth($mqSet,2); + $width2: null; @if (length($mqSet) == 3){ - $newWidth2: nth($mqSet,3); + $width2: nth($mqSet,3); } - @return calculateMQ($newRange, $newWidth1, $newWidth2); + @if ((str-index($range, 'outside') != null) and $isAndStatement){ + $error: 'All "outside" range types are incompatible with "plus" statements: (' + $mqSet + ')'; + @error $error; + } + + @return calculateMQ($range, $width1, $width2); } @function joinQueries($range, $queryString: ()){ - $isAndStatment: false; + $isAndStatement: false; @each $statement in $range { @if ($statement == 'plus') { - $isAndStatment: true; + $isAndStatement: true; } } @@ -149,7 +154,7 @@ $mq-em-base: 16px !default; //@include mq($MQ-example--single){} @if (isBasicQuery($range)){ - @return 'screen and ' + calculateBasicQuery($range); + @return 'screen and ' + calculateBasicQuery($range, $isAndStatement); //Uses this functionality if using a multi level MQ variable //eg: @@ -159,15 +164,16 @@ $mq-em-base: 16px !default; //; //@include mq($MQ-example--multiple){} } @else { + @debug $isAndStatement; @each $mqSet in $range { @if ($mqSet != 'plus') { @if (isBasicQuery($mqSet)){ - $newQuery: calculateBasicQuery($mqSet); + $newQuery: calculateBasicQuery($mqSet, $isAndStatement); - @if ($isAndStatment) { + @if ($isAndStatement) { @if ($queryString == ()){ $queryString: 'screen and ' + $newQuery; } @else { @@ -178,7 +184,7 @@ $mq-em-base: 16px !default; } } @else { - @if ($isAndStatment) { + @if ($isAndStatement) { $queryString: joinQueries($mqSet, $queryString); @if (length($queryString) > 1){ $error: 'mq-scss does not support "or" statements inside "plus" statements: ' + $queryString; From 0cab94e9a06e9edba510a6502a5389072807734a Mon Sep 17 00:00:00 2001 From: Daniel Tonon Date: Fri, 8 Sep 2017 00:46:25 +1000 Subject: [PATCH 10/13] Plus statement documentation update --- README.md | 91 +++++++++++++++++++++---------------------------------- 1 file changed, 34 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index 4389dfb..5190bd4 100644 --- a/README.md +++ b/README.md @@ -527,13 +527,13 @@ $MQ-mainHeading--red: ( You can also string multiple `plus` statements together: `````````````scss -$MQ-test1: (inside, 800px, 400px); -$MQ-test2: (max, 600px); +$MQ-a: (inside, 800px, 400px); +$MQ-b: (max, 600px); -$MQ-test3: ($MQ-test1 plus $MQ-test2 plus (min-ratio, '2 / 1')); +$MQ-c: ($MQ-a plus $MQ-b plus (min-ratio, '2 / 1')); .module { - @include mq($MQ-test3){ + @include mq($MQ-c){ background: red; } } @@ -548,68 +548,49 @@ $MQ-test3: ($MQ-test1 plus $MQ-test2 plus (min-ratio, '2 / 1')); } ````````````` -### !IMPORTANT! `plus` does not work in all situations +### !IMPORTANT! limitations of `plus` -There are some restrictions around using the `plus` keyword that you should be aware of. I haven't been able to write error messages for these edge cases warning users not to do these things. I didn't want the lack of error messages to hold back the feature though. +You should note that `plus` does not work in all situations. There are some restrictions around using the `plus` keyword that you should be aware of. #### It can not be used in conjunction with any `outside` range type -The following code **will not work** (at least not as expected) +The following code **will not work** and will throw an error stating that all `outside` range types (`outside`, `outside-height`, `outside-ratio`, `outside-device-ratio`) are incompatible with `plus` statements. `````````````scss -$MQ-test1: (outside, 800px, 400px); -$MQ-test2: (max, 600px); +$MQ-a: (outside, 800px, 400px); +$MQ-b: (max, 600px); -$MQ-test3: ($MQ-test1 plus $MQ-test2); +$MQ-c: ($MQ-a plus $MQ-b); .module { - @include mq($MQ-test3){ + @include mq($MQ-c){ background: red; } } ````````````` -`````````````css -/* outputted css: */ -/* (max-width: 400px) is on it's own, not affected by the (max-width: 600px) rule */ -@media screen and (max-width: 400px), screen and (min-width: 801px) and (max-width: 600px) { - .module { - background: red; - } -} -````````````` - -This issue occurs across all `outside` range types (`outside`, `outside-height`, `outside-ratio`, `outside-device-ratio`). #### `plus` statements can not contain any `or` statements -`Or` statements can contain `plus` statements however `plus` statements can not contain `or` statements. +`or` statements can contain `plus` statements however `plus` statements can not contain `or` statements. -The following code **will not work** (at least not as expected) +The following code **will not work** and will throw an error stating that `or` statements can't be placed inside `plus` statements. `````````````scss -$MQ-test1: ( +$MQ-a: ( (inside, 1200px, 800px), (max, 400px) ); -$MQ-test2: (max, 600px); +$MQ-b: (max, 600px); -$MQ-test3: ($MQ-test1 plus $MQ-test2); +$MQ-c: ($MQ-a plus $MQ-b); .module { - @include mq($MQ-test3){ + @include mq($MQ-c){ background: red; } } ````````````` -`````````````css -/* outputted css: */ -/* (max-width: 600px) rule is completely ignored */ -@media screen and (max-width: 1200px) and (min-width: 801px) { - .module { - background: red; - } -} -````````````` + #### Work arounds @@ -618,13 +599,13 @@ You can generally get around these issues by placing the `plus` statement inside So instead of this: `````````````scss -$MQ-test1: (outside, 800px, 400px); -$MQ-test2: (max, 600px); +$MQ-a: (outside, 800px, 400px); +$MQ-b: (max, 600px); -$MQ-test3: ($MQ-test1 plus $MQ-test2); +$MQ-c: ($MQ-a plus $MQ-b); .module { - @include mq($MQ-test3){ + @include mq($MQ-c){ background: red; } } @@ -633,41 +614,37 @@ $MQ-test3: ($MQ-test1 plus $MQ-test2); Do this: `````````````scss -$MQ-test1: (outside, 800px, 400px); -$MQ-test2: (max, 600px); - -/////////////////////////////////////////////////// Test is not working +$MQ-a: (outside, 800px, 400px); +$MQ-b: (max, 600px); -$MQ-test3: ( - ((min, 800px) plus $MQ-test2), - ((max, 400px) plus $MQ-test2) +$MQ-c: ( + ((max, 400px) plus $MQ-b), + ((min, 800px) plus $MQ-b) ); .module { - @include mq($MQ-test3){ + @include mq($MQ-c){ background: red; } } ````````````` `````````````css -/* Test is not working, should be outputting something different */ - -@media screen and (min-width: 801px) and (max-width: 600px) { +@media screen and (max-width: 400px) and (max-width: 600px), screen and (min-width: 801px) and (max-width: 600px) { .module { background: red; } } ````````````` -Also, as of version 1.2.0, you can utilise the native media query merging feature in Sass to sort out the media queries for you (as long as you don't mind them being outside of variables) +Alternatively, as of version 1.2.0, you can utilise the native media query merging feature in Sass to sort out the media queries for you (as long as you don't mind them being outside of variables). `````````````scss -$MQ-test1: (outside, 800px, 400px); -$MQ-test2: (max, 600px); +$MQ-a: (outside, 800px, 400px); +$MQ-b: (max, 600px); .module { - @include mq($MQ-test1){ - @include mq($MQ-test2){ + @include mq($MQ-a){ + @include mq($MQ-b){ background: red; } } From 38eb31ffd215d918dcee60c93882298675311e11 Mon Sep 17 00:00:00 2001 From: Daniel Tonon Date: Fri, 8 Sep 2017 01:05:39 +1000 Subject: [PATCH 11/13] changing all "and" references to "plus" and a couple other things --- README.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 5190bd4..2355287 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,9 @@ If you enjoy using mq-scss, try my new [mq-js](https://www.npmjs.com/package/mq- * [Naming your MQ variables](#naming-your-mq-variables) * [Creating your MQ variables](#creating-your-mq-variables) * [Combining media queries](#combining-media-queries) - * [Media query "or" statements](#media-query-or-statements) - * [Media Query "and" statements](#media-query-and-statements) + * [Media query `or` statements](#media-query-or-statements) + * [Media Query `plus` statements](#media-query-plus-statements) + * [**!IMPORTANT!** limitations of `plus`](#important-limitations-of-plus) * [em conversion](#em-conversion) * [Defining breakpoints](#defining-breakpoints) * [Bonus retina display mixin](#bonus-retina-display-mixin) @@ -188,7 +189,7 @@ It is easiest to think of ratio based media queries as always being based more o There are 2 types of ratio based media queries, "aspect-ratio" (shortened to just `ratio` in the mq mixin) and "device-aspect-ratio" (shortened to `device-ratio` in the mq mixin). It is generally best to stick with "aspect-ratio" rather than "device-aspect-ratio" since "aspect-ratio" is determined by the users browser window size. "device-aspect-ratio" is based on the physical screen size of the users device. With "aspect-ratio" you can see the effects by just resizing the browser window. With "device-aspect-ratio" you will physically have to look at the site on a different screen in order to see the effect... or look at the site with the Chrome dev tools screen emulator open (Chromes screen emulator obeys "device-aspect-ratio" media queries). -Ratio based media queries are mostly useful for when you have sites that have displays that take up the full screen. Displays like this tend to need media queries that understand both the users screen height and width at the same time. You may need to combine the ratio based media query with a more traditional pixel based media query for it to have the greatest effect. Read the [Media Query "and" statements](#media-query-and-statements) section for more details on how to do that. +Ratio based media queries are mostly useful for when you have sites that have displays that take up the full screen. Displays like this tend to need media queries that understand both the users screen height and width at the same time. You may need to combine the ratio based media query with a more traditional pixel based media query for it to have the greatest effect. Read the [Media Query `plus` statements](#media-query-plus-statements) section for more details on how to do that. ### Full list of media query ranges @@ -329,9 +330,9 @@ Well actually after gzipping, all the repetitive media query declarations [becom ## Combining media queries -### Media query "or" statements +### Media query `or` statements -Media Query "or" statements are only possible using an MQ variable. +Media Query `or` statements are only possible using an MQ variable. `````````````scss SASS: @@ -396,7 +397,7 @@ $MQ-element__color--alt: } ````````````` -### Media Query "and" statements +### Media Query `plus` statements So the scenario is that you have some styles you want to apply only when both the side bar is full width and the sub heading is hidden. This is the easiest way to do that: @@ -470,7 +471,7 @@ $MQ-subHeading--hidden: (inside, 800px, 400px); } ````````````` -It will even work as part of an "or" statement: +It will even work as part of an `or` statement: `````````````scss $MQ-subHeading--hidden: (inside, 800px, 400px); @@ -629,6 +630,7 @@ $MQ-c: ( } ````````````` `````````````css +/* outputted css */ @media screen and (max-width: 400px) and (max-width: 600px), screen and (min-width: 801px) and (max-width: 600px) { .module { background: red; @@ -696,7 +698,7 @@ $BP-page: 1200px; //styles go here } } -```````` +````````` ## Bonus retina display mixin @@ -747,6 +749,7 @@ To create this css: ### v1.3.0 - Added the `plus` keyword for improved handling of "and" statements +- Changed the breakpoints list example to just a list of variables ### v1.2.0 From e23e1e6e0b4c96ae4f6b0075f31e1fb2f772398d Mon Sep 17 00:00:00 2001 From: Daniel Tonon Date: Fri, 8 Sep 2017 01:06:56 +1000 Subject: [PATCH 12/13] Removing @debug statement --- _mq.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/_mq.scss b/_mq.scss index b5f95d2..d076972 100644 --- a/_mq.scss +++ b/_mq.scss @@ -164,7 +164,6 @@ $mq-em-base: 16px !default; //; //@include mq($MQ-example--multiple){} } @else { - @debug $isAndStatement; @each $mqSet in $range { From 3c4cd4e5f3f5ca624d59e90bcfe84ad5e5efe74e Mon Sep 17 00:00:00 2001 From: Daniel Tonon Date: Fri, 8 Sep 2017 01:11:03 +1000 Subject: [PATCH 13/13] Updating pkg.json version number --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index ad4c8f4..99a27aa 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "mq-scss", - "version": "1.2.4", - "description": "An extreamly powerful but easy to use Sass media query mixin", + "version": "1.3.0", + "description": "An extremely powerful but easy to use Sass media query mixin", "main": "_mq.scss", "repository": { "type": "git",