A fine API to manage media queries in JS with ease
fine-mq
A fine API to manage media queries in JS with ease. It has first-class integration with VueJS.
Installation
- Using NPM/Yarn
npm i fine-mq
# or
yarn add fine-mq
- Using unpkg.com
<script src="https://unpkg.com/fine-mq/dist/fine-mq.min.js"></script>
This main file exposes a install
function (Vue plugin), a Mq
class and toMqString
function.
These are all the builds available via unpkg:
https://unpkg.com/fine-mq/dist/fine-mq.min.js
(umd)https://unpkg.com/fine-mq/dist/fine-mq.cjs.js
(commonjs)https://unpkg.com/fine-mq/dist/fine-mq.es.js
(es module)https://unpkg.com/fine-mq/dist/mq.min.js
(umd)https://unpkg.com/fine-mq/dist/mq.cjs.js
(commonjs)https://unpkg.com/fine-mq/dist/mq.es.js
(es module)https://unpkg.com/fine-mq/dist/to-mq-string.min.js
(umd)https://unpkg.com/fine-mq/dist/to-mq-string.cjs.js
(commonjs)https://unpkg.com/fine-mq/dist/to-mq-string.es.js
(es module)
Usage
This package is a set of three libs.
- A vue plugin that exposes useful properties on vue instances, a component and a directive. This lib includes the two others. (
FineMq
) - A media query interface that lets you add/remove aliases to media queries, register/unregister handlers that listen to matching state of a media query or alias. (
Mq
) - A lib that exposes a single function that helps you transorm a media query written as a JavaScript object to its string value. (
toMqString
)
Vue plugin
Define your breakpoints and/or aliases to media queries and start using them with Vue.
import Vue from 'vue'
import FineMq from 'fine-mq'
import App from './App'
// Define your aliases
// NOTE: any size can be expressed as a number, anything that can be casted to a finite number, Infinity, or a size in 'px' or 'em' unit.
Vue.use(FineMq, {
aliases: {
sm: 680, // <=> [0, 680]
md: [681, 1024],
lg: [1025], // <=> [1025, Infinity]
landscape: '(orientation: landscape)',
an_alias_name: {
screen: true,
minWidth: '23em',
maxWidth: '768px'
}
}
})
// or just Vue.use(FineMq). By default aliases are { sm: 680, md: [681, 1024], lg: [1025] }
// => This will register the following aliases:
// {
// sm: '(max-width: 680px)',
// 'sm+': '(min-width: 681px)',
// md: '(min-width: 681px) and (max-width: 1024px)',
// 'md+': '(min-width: 1025px)',
// 'md!': '(min-width: 681px)',
// lg: '(min-width: 1025px)',
// landscape: '(orientation: landscape)',
// an_alias_name: 'screen and (min-width: 380px) and (max-width: 768px)'
// }
// Three reactive properties will then be available on Vue instances:
// - `$mqAliases` is an object that contains the matching state for each alias in the form { [alias]: true/false }.
// - `$lastActiveMQAlias` will contains the last alias that matched and triggered the listener.
// - `$mq` is a Mq instance. See below.
NOTE 1: Absurd modifiers will not be created (ex: when the lower bound is 0, there is no need for the «!» modifier, or if the upper bound is Infinity, there is no need for both «!» and «+» modifiers).
NOTE 2: If you specify the unit for your size, the + 1
operation will not be performed.
Then in your templates, you can use the globally defined MqShow
component or v-mq-show-if
directive.
- The
MqShow
component facilitate conditional rendering with media queries. It renders its content only when one fo the given media queries matches. Cons: If the slot content has multiple root nodes, they will be wrapped by a DIV tag but you can customize the wrapper tag to render with thetag
prop.
The if
prop if required and can be a String/Array/Object. Modifiers are also supported (for both the component and the directive) by appending it at the end of the alias («+» for greater breakpoints, «!» for current AND greater breakpoints).
<MqShow if="sm">
<!-- slot ...-->
</MqShow>
<!-- or -->
<MqShow if="only screen and (min-width: 200px)"> <!-- any valid media query works -->
<!-- slot ... -->
</MqShow>
<!-- or -->
<MqShow if="md+"> <!-- The `+` modifier here means «above md» -->
<!-- slot ... -->
</MqShow>
<!-- or -->
<MqShow if="md!"> <!-- The `!` modifier here means «md AND above md» -->
<!-- slot ... -->
</MqShow>
<!-- or -->
<MqShow :if="['sm', 'md+']"> <!-- array of aliases (with or without modifier) or valid media queries -->
<!-- slot ... -->
</MqShow>
<!-- or -->
<MqShow :if="{orientation: 'landscape'}"> <!-- object notation is also supported -->
<!-- slot ... -->
</MqShow>
<!-- or -->
<MqShow if="an_alias_name">
<!-- slot ... -->
</MqShow>
- The
v-mq-show-if
directive sets the display property of the bound element's style to'none'
only when none of the given media queries matches. Cons: The rendered element will still appear in the DOM tree even if not displayed. Usage is the same as for the component.
<div v-mq-show-if="'sm'">...</div>
<!-- or -->
<div v-mq-show-if="'only screen and (min-width: 200px)'">...</div>
<!-- or -->
<div v-mq-show-if="'md+'">...</div>
<!-- or -->
<div v-mq-show-if="'md!'">...</div>
<!-- or -->
<div v-mq-show-if="['sm', 'md+']">...</div>
<!-- or -->
<div v-mq-show-if="{orientation: 'landscape'}">...</div>
<!-- or -->
<div v-mq-show-if="'an_alias_name'">...</div>
Mq
API
The Mq
class exposes a fine API that lets you handle media query changes on your page on the JavaScript side.
Usage example
import Mq from 'fine-mq/dist/mq'
const mq = new Mq({
small: 'only screen and (max-width: 480px)',
medium: 'only screen and (min-width: 480px) and (max-width: 720px)'
})
const defaultColor = '#FFF'
const changeColor = color => ({matcher, alias}) => {
document.body.style.backgroundColor = matcher.mathces ? color : defaultColor
}
mq.on('small', changeColor('blue'))
mq.on('medium', changeColor('green'))
mq.on('only screen and (min-width: 720px)', changeColor('red'))
API description
const mq = new Mq(aliases)
Initialise a new Mq
. Aliases can be registered afterwards.
mq.alias(alias[, query]) / mq.unalias(alias)
Register an alias
for a query
, or register multiple aliases at once via an object.
mq.alias('small', '(max-width: 100px)')
mq.alias({
small: '(max-width: 100px)',
medium: '(max-width: 200px)'
})
Then you can unregister previously registered aliases with mq.unalias(alias)
.
mq.on(query, callback, context)
Register a callback
which will be executed with the given context
everytime the match state (true/false) for a query or alias. If context
is not specified, it will default to the mq
instance.
// `alias` is the given alias or query and `matcher` is the matchMedia object for that query.
mq.on('(max-width: 400px)', ({matcher, alias}) => {})
mq.on('smartphones', ({matcher, alias}) => {}, {})
mq.off(query, callback, context)
Remove all callbacks for all queries:
mq.off()
Remove all callbacks for a query
or alias:
mq.off('(max-width: 400px)')
Remove a specific callback
for a query
or alias:
mq.off('(max-width: 400px)', () => {})
Remove a specific callback
with a specific context
for a query
or alias:
mq.off('(max-width: 400px)', () => {}, {})
Tip: All methods (alias
, unalias
, on
and off
) return this
so that you can chain them.
NOTE: The Mq class is written independantly from toMqString
meaning that the object-style notation for queries is not supported here.
toMqString
This function can generate a valid media query string from a javascript object.
Usage example
import toMqString from 'fine-mq/dist/to-mq-string'
toMqString({minWidth: 100, maxWidth: 200});
// -> '(min-width: 100px) and (max-width: 200px)'
Media type
toMqString({screen: true}); // -> 'screen'
Media type with negation
toMqString({handheld: false}); // -> 'not handheld'
Media features can be specified in camel case
toMqString({minWidth: 100, maxWidth: 200});
// -> '(min-width: 100px) and (max-width: 200px)'
px is added to numeric dimension values
toMqString({minWidth: 100, maxWidth: '20em'});
// -> '(min-width: 100px) and (max-width: 20em)'
Multiple media queries can be passed as an array
toMqString([{screen: true, minWidth: 100}, {handheld: true, orientation: 'landscape'}]);
// -> 'screen and (min-width: 100px), handheld and (orientation: landscape)'
NOTE: When passing an array to the Vue component/directive and you want it to be considered as one media query like in the last example, remember to wrap it in another array like this: [[{screen: true, minWidth: 100}, {handheld: true, orientation: 'landscape'}]]
(that will register a single listener for 'screen and (min-width: 100px), handheld and (orientation: landscape)' instead of multiple listeners, one for 'screen and (min-width: 100px)' and another for 'handheld and (orientation: landscape)' )