├── .gitignore ├── .travis.yml ├── _query.scss ├── tests ├── tests.scss ├── _query.scss └── _api.scss ├── bower.json ├── query ├── utils │ └── _quick-sort.scss ├── _defaults.scss ├── _query.scss └── _api.scss ├── package.json ├── Gruntfile.js ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .sass-cache/ 2 | node_modules/ 3 | bower_components/ 4 | tests/*.css 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.10 4 | before_install: 5 | - gem install sass 6 | before_script: 7 | - npm install grunt-cli -g 8 | -------------------------------------------------------------------------------- /_query.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Query function 3 | /// 4 | /// @author Nicolas Müller Noulezas (http://nicolasmn.de) 5 | /// 6 | 7 | 8 | @import "query/query"; 9 | -------------------------------------------------------------------------------- /tests/tests.scss: -------------------------------------------------------------------------------- 1 | @import "../query"; 2 | 3 | @import "bootcamp"; 4 | $bc-setting-verbose: true; 5 | 6 | @include runner-start; 7 | @import "query"; 8 | @import "api"; 9 | @include runner-end; 10 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scss-query", 3 | "authors": [ 4 | "Nicolas Müller Noulezas " 5 | ], 6 | "description": "Simple and memorizeable @media queries helper function", 7 | "main": "_query.scss", 8 | "keywords": [ 9 | "scss", 10 | "css", 11 | "media", 12 | "query", 13 | "function" 14 | ], 15 | "license": "MIT", 16 | "ignore": [ 17 | ".gitignore", 18 | ".travis.yml", 19 | "Gruntfile.js", 20 | "package.json", 21 | "tests" 22 | ], 23 | "dependencies": {} 24 | } 25 | -------------------------------------------------------------------------------- /query/utils/_quick-sort.scss: -------------------------------------------------------------------------------- 1 | /// Quick sort 2 | /// @author Sam Richards 3 | /// @param {List} $list - list to sort 4 | /// @return {List} 5 | @function quick-sort($list) { 6 | $less: (); 7 | $equal: (); 8 | $large: (); 9 | 10 | @if length($list) > 1 { 11 | $seed: nth($list, ceil(length($list) / 2)); 12 | 13 | @each $item in $list { 14 | @if ($item == $seed) { 15 | $equal: append($equal, $item); 16 | } @else if ($item < $seed) { 17 | $less: append($less, $item); 18 | } @else if ($item > $seed) { 19 | $large: append($large, $item); 20 | } 21 | } 22 | 23 | @return join(join(quick-sort($less), $equal), quick-sort($large)); 24 | } 25 | 26 | @return $list; 27 | } 28 | -------------------------------------------------------------------------------- /query/_defaults.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Defaults 3 | /// 4 | /// @group defaults 5 | /// 6 | 7 | /// Base font size used for em conversion. 8 | /// 9 | /// @type {Number} 10 | $base-font-size: 16px !default; 11 | 12 | 13 | /// Default breakpoints for common screen sizes. 14 | /// Stores the sizes used by `query` function. 15 | /// 16 | /// @access public 17 | /// @type Map 18 | /// 19 | /// @prop {Number} breakpoints.micro - Smartwatches 20 | /// @prop {Number} breakpoints.mini - Smartphones 21 | /// @prop {Number} breakpoints.small - Tablets 22 | /// @prop {Number} breakpoints.medium - Notebooks 23 | /// @prop {Number} breakpoints.large - Laptops 24 | /// @prop {Number} breakpoints.huge - TVs 25 | /// @prop {Number} breakpoints.giant - Retina sickness 26 | $query-breakpoints: ( 27 | 'micro': 320px, 28 | 'mini': 480px, 29 | 'small': 768px, 30 | 'medium': 960px, 31 | 'large': 1440px, 32 | 'huge': 2560px, 33 | 'giant': 5120px 34 | ) !default; 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scss-query", 3 | "version": "1.0.1", 4 | "description": "Simple and memorizeable @media queries helper function", 5 | "main": "_query.scss", 6 | "directories": { 7 | "test": "tests" 8 | }, 9 | "dependencies": {}, 10 | "devDependencies": { 11 | "bootcamp": "^1.1.7", 12 | "grunt-contrib-sass": "^0.8.1", 13 | "grunt-contrib-watch": "^0.6.1", 14 | "grunt": "^0.4.5" 15 | }, 16 | "scripts": { 17 | "test": "npm install && grunt test" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/nicolasmn/scss-mediaquery.git" 22 | }, 23 | "keywords": [ 24 | "scss", 25 | "css", 26 | "media", 27 | "query", 28 | "function" 29 | ], 30 | "author": "Nicolas Müller Noulezas (http://nicolasmn.de)", 31 | "license": "MIT", 32 | "bugs": { 33 | "url": "https://github.com/nicolasmn/scss-mediaquery/issues" 34 | }, 35 | "homepage": "https://github.com/nicolasmn/scss-mediaquery" 36 | } 37 | -------------------------------------------------------------------------------- /tests/_query.scss: -------------------------------------------------------------------------------- 1 | 2 | @include describe("Query [function]") { 3 | @include it("should return the queried breakpoint width") { 4 | // Test defaults 5 | @include should( expect( query(min, mini) ), to( equal('min-width: 480px') )); 6 | 7 | // Test with unordered map 8 | @include query(init, ( 9 | 'huge': 2560px, 10 | 'mini': 480px, 11 | 'medium': 960px, 12 | 'small': 768px, 13 | 'giant': 5120px, 14 | 'micro': 320px, 15 | 'large': 1440px 16 | )); 17 | @include should( expect( query(only, mini) ), to( equal('min-width: 480px) and (max-width: 767px') )); 18 | 19 | // Test with `init` 20 | @include query(init, (tall: 500px, grande: 900px)); 21 | 22 | @include should( expect( query(min, tall) ), to( equal('min-width: 500px') )); 23 | @include should( expect( query(max, tall) ), to( equal('max-width: 499px') )); 24 | @include should( expect( query(only, tall) ), to( equal('min-width: 500px) and (max-width: 899px') )); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 3 | // Modules 4 | grunt.loadNpmTasks('grunt-contrib-sass'); 5 | grunt.loadNpmTasks('grunt-contrib-watch'); 6 | grunt.loadNpmTasks('bootcamp'); 7 | 8 | // Grunt Tasks 9 | grunt.initConfig({ 10 | 11 | // Sass 12 | sass: { 13 | test: { 14 | options: { 15 | style: 'expanded', 16 | loadPath: './node_modules/bootcamp/dist', 17 | sourcemap: 'none' 18 | }, 19 | files: [{ 20 | src: 'tests/tests.scss', 21 | dest: 'tests/results.css' 22 | }] 23 | } 24 | }, 25 | 26 | // Bootcamp 27 | bootcamp: { 28 | test: { 29 | files: { 30 | src: ['tests/results.css'] 31 | } 32 | } 33 | }, 34 | 35 | // Watch 36 | watch: { 37 | dist: { 38 | files: ['{*/,}*.scss'], 39 | tasks: ['sass', 'bootcamp'] 40 | } 41 | } 42 | }); 43 | 44 | // Tasks 45 | grunt.registerTask('test', ['sass', 'bootcamp']); 46 | grunt.registerTask('default', ['test']); 47 | }; 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Nicolas Müller Noulezas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /query/_query.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Function 3 | /// 4 | /// @group main 5 | /// 6 | 7 | 8 | @import "defaults"; 9 | @import "api"; 10 | 11 | 12 | /// Build a query string by given keyword and key in `$query-breakpoints`. 13 | /// 14 | /// @access public 15 | /// @type Function 16 | /// 17 | /// @require $query-breakpoints 18 | /// 19 | /// @param {String} $mode - Either `min`, `max` or `only` 20 | /// @param {mapKey} $size - Key in `$query-breakpoints` 21 | /// @return {Query} - Query string 22 | /// 23 | /// @example Scss - Query modes 24 | /// @media (query(min, mini)) { ... } 25 | /// @media (query(max, medium)) { ... } 26 | /// @media (query(only, small)) { ... } 27 | /// 28 | /// @example Css - Output 29 | /// @media (min-width: 480px) { ... } 30 | /// @media (max-width: 959px) { ... } 31 | /// @media (min-width: 480px) and (max-width: 959px) { ... } 32 | /// 33 | @function query($mode, $size) { 34 | 35 | // Store reduction value as variable for conversion 36 | $reduce: 1px; 37 | 38 | // Convert `$reduce` to breakpoint value if `$reduce` 39 | // is needed later on and units are not equal 40 | @if index((max, only), $mode) { 41 | 42 | $unit: unit(query-get($size)); 43 | 44 | @if $unit != unit($reduce) { 45 | 46 | // Check if a function named after breakpoint unit 47 | // exists and call it 48 | @if function-exists($unit) { 49 | $reduce: call($unit, $reduce); 50 | } 51 | 52 | // Handle conversion if breakpoint unit is in em 53 | @elseif $unit == em { 54 | $reduce: ($reduce / $base-font-size) * 1em; 55 | } 56 | 57 | @else { 58 | @error "Can not convert #{$reduce} to #{unit(query-get($size))}."; 59 | @return null; 60 | } 61 | } 62 | } 63 | 64 | // Test if key exists 65 | @if query-exists($size) { 66 | 67 | // Greater than breakpoint 68 | @if min == $mode { 69 | @return "min-width: #{query-get($size)}"; 70 | } 71 | 72 | // Smaller than breakpoint 73 | @if max == $mode { 74 | @return "max-width: #{query-get($size) - $reduce}"; 75 | } 76 | 77 | // Only breakpoint 78 | @if only == $mode { 79 | 80 | $capper: query-get-next($size); 81 | 82 | @if $capper { 83 | @return "#{query(min, $size)}) and (#{query(max, $capper)}"; 84 | } 85 | 86 | @error "‘#{$size}’ is the last key in current context."; 87 | @return null; 88 | } 89 | 90 | @error "‘#{$mode}’ is not a valid mode. " + 91 | "Valid modes are: min, max, only."; 92 | } 93 | 94 | @error "There is no key ‘#{$size}’ in the current context. " + 95 | "Valid keys are: #{map-keys(query-get())}."; 96 | } 97 | -------------------------------------------------------------------------------- /tests/_api.scss: -------------------------------------------------------------------------------- 1 | 2 | @include describe("Query Exists [function]") { 3 | @include it("should return true if query width exists and false if not") { 4 | @include query(init, ("test": 500px)); 5 | 6 | @include should( expect( query-exists(test) ), to( be-truthy() )); 7 | @include should( expect( query-exists(noop) ), to( be-falsy() )); 8 | } 9 | } 10 | 11 | 12 | 13 | @include describe("Query Get [function]") { 14 | @include it("should return queried breakpoint width or null if not found") { 15 | @include query(init, ("test": 500px)); 16 | 17 | @include should( expect( query-get(test) ), to( equal(500px) )); 18 | @include should( expect( query-get(noop) ), to( be-null() )); 19 | } 20 | } 21 | 22 | 23 | 24 | @include describe("Query [mixin]") { 25 | 26 | // Test reset 27 | @include it("Reset should reset the context") { 28 | @include query(reset); 29 | @include should( expect( query-get() ), to( be-null() )); 30 | } 31 | 32 | 33 | // Test init 34 | @include it("Init should initialze the context") { 35 | $map: ("test": 500px); 36 | @include query(init, $map); 37 | @include should( expect( query-get() ), to( equal($map) )); 38 | } 39 | 40 | 41 | // Test use 42 | @include it("Use should add the given breakpoints to context while in content") { 43 | $map: ("test": 500px, "test1": 600px); 44 | @include query(init, $map); 45 | @include query(use, current, 500px) { 46 | @include should( expect( query-get(current) ), to( equal(500px) )); 47 | } 48 | @include should( expect( query-get() ), to( equal($map) )); 49 | } 50 | 51 | 52 | // Test add 53 | @include it("Add should add new breakpoints to context") { 54 | @include query(reset); 55 | 56 | // add single breakpoint 57 | @include query(add, test, 500px); 58 | @include should( expect( query-get(test) ), to( equal(500px) )); 59 | @include query(reset); 60 | 61 | // add multible breakpoints 62 | $map: (test1: 400px, test2: 500px); 63 | @include query(add, $map); 64 | @include should( expect( query-get() ), to( equal($map) )); 65 | } 66 | 67 | 68 | // Test remove 69 | @include it("Remove should remove existing breakpoints from context") { 70 | @include query(reset); 71 | @include query(add, (test1: 500px, test2: 600px, test3: 700px)); 72 | 73 | // remove multible breakpoints 74 | @include query(remove, test1, test2); 75 | @include should( expect( query-get() ), to( equal(("test3": 700px)) )); 76 | 77 | // remove single breakpoint 78 | @include query(remove, test3); 79 | @include should( expect( query-get() ), to( be-null() )); 80 | } 81 | 82 | 83 | // Test set 84 | @include it("Set should set existing breakpoints in context") { 85 | @include query(reset); 86 | @include query(add, (test: 500px, test1: 500px, test2: 600px)); 87 | 88 | // set one breakpoint 89 | @include query(set, test, 800px); 90 | @include should( expect( query-get(test) ), to( equal(800px) )); 91 | @include query(remove, test); 92 | 93 | // set multible breakpoints 94 | $map: ("test1": 700px, "test2": 800px); 95 | @include query(set, $map); 96 | @include should( expect( query-get() ), to( equal($map) )); 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Scss Query [![](https://travis-ci.org/nicolasmn/scss-mediaquery.svg)](https://travis-ci.org/nicolasmn/scss-mediaquery) [![](https://david-dm.org/nicolasmn/scss-mediaquery/dev-status.svg)](https://david-dm.org/nicolasmn/scss-mediaquery#info=devDependencies) 2 | 3 | Simple and memorizeable `@media` queries helper function; build query strings defining a [mode](#parameter-mode) and a breakpoint name. 4 | 5 | _Example:_ 6 | ```scss 7 | // Set custom breakpoints 8 | $query-breakpoints: ( 9 | 'tall': 500px, 10 | 'grande': 900px, 11 | 'venti': 1400px 12 | ); 13 | 14 | // @import query function 15 | 16 | // Profit 17 | @media (query(min, tall)) { 18 | body::before { content: "only at screens with a min-width of 500px"; } 19 | } 20 | ``` 21 | 22 | **Protip**: Use `@include query(init, $query-breakpoints)` to (re)initialize `query()` function whenever you want. Refer to the [API](#api) for an overview of all the available runtime manipulation mixins. 23 | 24 | 25 | ## Installation 26 | 27 | The recommended installation method is Bower but you can manually install `query()` function as well. 28 | 29 | ### Install using NPM 30 | ```bash 31 | $ npm install --save scss-query 32 | ``` 33 | 34 | ### Install using Bower 35 | ```bash 36 | $ bower install --save scss-query 37 | ``` 38 | 39 | ### Install via file download 40 | You can always just [download this repository](https://github.com/nicolasmn/scss-mediaquery/archive/master.zip) into your project and `@import` it. 41 | 42 | 43 | ## Usage 44 | 45 | The initial example should cover pretty much everything there is to know for basic usage. But let's quick-start you even more: 46 | 47 | ### with [Bootstrap](http://getbootstrap.com/) 48 | ```scss 49 | // v4 50 | $query-breakpoints: $grid-breakpoints; 51 | 52 | // v3 53 | $query-breakpoints: ( 54 | 'xs': $screen-xs-min, 55 | 'sm': $screen-sm-min, 56 | 'md': $screen-md-min, 57 | 'lg': $screen-lg-min 58 | ); 59 | ``` 60 | 61 | ### with [Foundation](http://foundation.zurb.com/) 62 | ```scss 63 | // v6 64 | $query-breakpoints: $breakpoints; 65 | ``` 66 | 67 | 68 | ## Function `query(` `mode` `, ` `size` `)` 69 | 70 | ### Parameter `mode` 71 | 72 | Mode can be one of the following: 73 | 74 | - `min` - Min breakpoints 75 | - `max` - Max breakpoints 76 | - `only` - Min and max breakpoints 77 | 78 | In case mode is `max` or `only` 1px will be substracted from max breakpoint value to prevent conflicting breakpoints. If breakpoint value is not in pixels, conversion will be handled trough a function named after breakpoint value unit – for `em` there is a fallback integrated. 79 | 80 | If mode is `only`, max size will be next breakpoint in context. 81 | 82 | ### Parameter `size` 83 | 84 | Key in `$query-breakpoints`. 85 | 86 | `query()` function ships with a list of common screen sizes as default breakpoints. These are just for a quick start and it is recommended to change them to fit your design. 87 | 88 | #### Defaults 89 | Defaults are in pixels. However, if you want to use another value like `em` you can do that too. Remember to set `$query-breakpoints` **before** importing `query()` function in order to overwrite the defaults. 90 | 91 | ```scss 92 | $query-breakpoints: ( 93 | 'micro': 320px, 94 | 'mini': 480px, 95 | 'small': 768px, 96 | 'medium': 960px, 97 | 'large': 1440px, 98 | 'huge': 2560px, 99 | 'giant': 5120px 100 | ) !default; 101 | ``` 102 | 103 | 104 | ## API 105 | 106 | The API allows for runtime manipulation of `query()` context. The following methods are provided as mixin via `@include query($method, $args...)`: 107 | 108 | | Method | Description 109 | |----------|------------ 110 | | `add` | Add a new breakpoint 111 | | `remove` | Remove an existing breakpoint 112 | | `set` | Set one or multible existing breakpoints at once 113 | | `reset` | Reset context (aka remove all breakpoints) 114 | | `init` | Initialise query function by passing a map 115 | | `use` | Use a breakpoint while in mixin's `@content` 116 | 117 | See [query/_api.scss](query/_api.scss) for documentation on the individual mixins. 118 | 119 | 120 | ## Contributing 121 | 122 | As an open-source project, contributions are more than welcome, they're extremely helpful and actively encouraged. In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using [Grunt](http://gruntjs.com/). If you see any room for improvement, open an [issue](https://github.com/nicolasmn/scss-query/issues) or submit a [pull request](https://github.com/nicolasmn/scss-query/pulls). 123 | 124 | 125 | ## License 126 | 127 | This project is provided under the terms of the [MIT License](LICENSE). 128 | 129 | 130 | --- 131 | 132 | Authored by **Nicolas Müller Noulezas** · [Github](https://github.com/nicolasmn) · [Twitter](https://twitter.com/nicolasmn) · [CodePen](https://codepen.io/nicolasmn) 133 | -------------------------------------------------------------------------------- /query/_api.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// API 3 | /// 4 | /// @group api 5 | /// 6 | 7 | 8 | @import "utils/quick-sort"; 9 | 10 | 11 | /// Context used to get/set breakpoint widths 12 | /// @access private 13 | /// @type {map} 14 | $_query-context: $query-breakpoints; 15 | 16 | 17 | /// Test if a breakpoint exists 18 | /// @param {string} $name - Name of breakpoint 19 | /// @return {bool} - True if breakpoint exists 20 | @function query-exists($name) { 21 | @return map-has-key($_query-context, $name); 22 | } 23 | 24 | 25 | /// Sort query map by values 26 | /// @param {map} $map - Map to sort 27 | /// @return {map} - Sorted map 28 | @function query-sort($map) { 29 | $vals: quick-sort(map-values($map)); 30 | $keys: map-keys($map); 31 | $sort: (); 32 | 33 | @each $key, $val in $map { 34 | $keys: set-nth($keys, index($vals, $val), $key); 35 | } 36 | 37 | @for $i from 1 through length($keys) { 38 | $sort: map-merge($sort, (nth($keys, $i): nth($vals, $i))); 39 | } 40 | 41 | @return $sort; 42 | } 43 | 44 | 45 | /// Get a breakpoint by its name 46 | /// @param {string} $name: null - Name of the breakpoint 47 | /// @return {number|map} - Returns either the whole context or the breakpoint asked for 48 | @function query-get($name: null) { 49 | // No name to search for, return whole context if it's not empty and sort it 50 | // from smallest to tallest 51 | @if not $name { 52 | @if length($_query-context) > 0 { 53 | @return query-sort($_query-context); 54 | } 55 | @return null; 56 | } 57 | // Check if query name exists in context 58 | @if query-exists($name) { 59 | @return map-get($_query-context, $name); 60 | } 61 | 62 | @warn "Breakpoint ‘#{$name}’ does not exist."; 63 | @return null; 64 | } 65 | 66 | 67 | /// Get next bigger breakpoint in context 68 | /// @param {string} $name - Name of the breakpoint to start from 69 | /// @return {number|null} - Next bigger breakpoint or null 70 | @function query-get-next($name) { 71 | $keys: map-keys(query-get()); 72 | $nextIndex: index($keys, $name) + 1; 73 | 74 | @if $nextIndex <= length($keys) { 75 | @return nth($keys, $nextIndex); 76 | } 77 | 78 | @return null; 79 | } 80 | 81 | 82 | /// Shorthand for query-* mixins 83 | /// @param {string} $method - Method to include 84 | /// @param {mixed} $args... - Arguments to pass the mixin 85 | @mixin query($method, $args...) { 86 | @if mixin-exists("query-#{$method}") { 87 | @if $method == "init" { @include query-init($args...) } 88 | @if $method == "add" { @include query-add($args...) } 89 | @if $method == "remove" { @include query-remove($args...) } 90 | @if $method == "set" { @include query-set($args...) } 91 | @if $method == "reset" { @include query-reset($args...) } 92 | @if $method == "use" { @include query-use($args...) { @content } } 93 | } 94 | 95 | @else { 96 | @warn "Method ‘#{$method}’ is not a valid api method for query."; 97 | } 98 | } 99 | 100 | 101 | /// Add a new breakpoint 102 | /// @param {string|map} $name - Name or map of names and widths of the breakpoint(s) to add 103 | /// @param {number} $width: null - Width of a single breakpoint to add 104 | /// @param {bool} $force - Overwrite existing breakpoints? Default is false 105 | @mixin query-add($name, $width: null, $force: false) { 106 | // Name is a map and no width given, call query-add on each map item 107 | @if type-of($name) == map and not $width { 108 | @each $_name, $_width in $name { 109 | @include query-add($_name, $_width, $force); 110 | } 111 | } 112 | // Validate name and width 113 | @elseif type-of($name) == string and type-of($width) == number { 114 | // Merge name and width into context if forced to or breakpoint does not 115 | // exist 116 | @if $force or not query-exists($name) { 117 | $_query-context: map-merge($_query-context, ($name: $width)) !global; 118 | } 119 | 120 | @else { 121 | @warn "Breakpoint ‘#{$name}’ (#{map-get($_query-context, $name)}) " + 122 | "already exists."; 123 | } 124 | } 125 | // Validation failed 126 | @else { 127 | @if type-of($name) != string { 128 | @warn "Parameter ‘name’ must be a string. Was #{type-of($name)}."; 129 | } 130 | @if type-of($width) != number { 131 | @warn "Parameter ‘width’ must be a number. Was #{type-of($width)}."; 132 | } 133 | } 134 | } 135 | 136 | 137 | /// Remove an existing breakpoint 138 | /// @param {string} $names... - Name(s) of the breakpoint(s) to remove 139 | @mixin query-remove($names...) { 140 | // Remove existing breakpoints 141 | @each $name in $names { 142 | @if query-exists($name) { 143 | $_query-context: map-remove($_query-context, $name) !global; 144 | } 145 | 146 | @else { 147 | @warn "Breakpoint ‘#{$name}’ does not exist."; 148 | } 149 | } 150 | } 151 | 152 | 153 | /// Set one or multible existing breakpoints at once 154 | /// @param {string|map} $name - Name or map of names and widths of the breakpoint(s) to set 155 | /// @param {number} $width: null - Width of single brakpoint to set 156 | @mixin query-set($name, $width: null) { 157 | // Both parameters given, overwrite width of existing breakpoint 158 | @if query-exists($name) and $width { 159 | @include query-add($name, $width, true); 160 | } 161 | // No width given, check if first parameter is a map 162 | @elseif type-of($name) == map { 163 | // Overwrite each existing breakpoint in context 164 | @each $_name, $_width in $name { 165 | @if query-exists($_name) { 166 | @include query-add($_name, $_width, true); 167 | } 168 | } 169 | } 170 | } 171 | 172 | 173 | /// Reset context (aka remove all breakpoints) 174 | @mixin query-reset() { 175 | $names: map-keys($_query-context); 176 | @include query-remove($names...); 177 | } 178 | 179 | 180 | /// Initialise query function by passing a map 181 | /// @param {map} $map - Map of breakpoint names and widths to initialise with 182 | @mixin query-init($map) { 183 | // Clean up 184 | @include query-reset; 185 | // Add each breakpoint to context 186 | @each $name, $width in $map { 187 | @include query-add($name, $width); 188 | } 189 | } 190 | 191 | 192 | /// Use a breakpoint while in mixin's `@content` 193 | /// @param {string|map} $name - Name or map of names and widths of the breakpoint(s) to add 194 | /// @param {number} $width: null - Width of a single breakpoint to add 195 | @mixin query-use($name, $width: null) { 196 | $context: query-get(); 197 | @include query-add($name, $width, true); 198 | 199 | @content; 200 | 201 | @include query-init($context); 202 | } 203 | --------------------------------------------------------------------------------