Media Query Helper
This package is intended for a React development environment and makes it able to either style (using styles components) or listen to a resize event using a custom hook.
Styling
The mq()
utiltiy will resolve styling using a similar syntax as used within styled components. It can output a @media
query and follows a familiar syntax. The mq()
utility will resolve any styling (and props) used inside a styled component. This means that any arguments will automatically be parsed and returned as a concatenated string.
Getting started
You can use the mq() utility as long as it's imported in your component. The syntax for styling is simple, options (arguments) will be discussed below. The following example shows a a simple styled component using Emotion.
import { mq } from '@greenberry/mq';
const Example = styled('div')`
width: 50%;
height: auto;
display: block;
${mq('tablet')`
width: 75%;
`}
${mq(['phone', 'tablet-small'])`
width: 100%;
`}
`;
This will parse the component to have the following styling, class name will normally be generated by Emotion.
.example {
width: 50%;
height: auto;
display: block;
@media screen (min-width: 0rem) and (max-width: 60rem) {
width: 75%;
}
@media screen (min-width: 27.5rem) and (max-width: 47.5rem) {
width: 75%;
}
}
Breakpoint configuration
The mq()
util offers multiple options to resolve a media query. By default the utiltiy will use a default setup for breakpoints. This configuration can be overruled using the ViewProvider
. You can also pass a value as a string or number. It's however best to use named values for consistancy.
const namesConfig = {
desktopWide: 1440,
desktop: 1280,
tabletWide: 1080,
tablet: 960,
tabletSmall: 760,
phablet: 640,
phoneWide: 560,
phone: 420,
};
The values in this config resemble a px
unit, using the mq()
function, numbers will be parsed to rem
values. For instance (with a base font-size of 16px) the value 1280
will resolve as 80rem
. The following table shows the rem
values of the default configuration.
value | number | rem |
---|---|---|
desktopWide | 1440 | 90rem |
desktop | 1280 | 80rem |
tabletWide | 1080 | 67.rem |
tablet | 960 | 60rem |
tabletSmall | 760 | 47.5rem |
phablet | 640 | 40rem |
phoneWide | 560 | 35rem |
phone | 440 | 27.5rem |
Besides named values (default), there is also a shorthand version available. This can be configured using the ViewProvider
.
const shorthandConfig = {
xxl: 1440,
xl: 1280,
l: 1080,
m: 880,
s: 640,
xs: 560,
xxs: 420,
};
Overrule configuration
The default configuration can be overruled setting your own config as a prop on the ViewProvider
. Inside your main App
component set the provider so the mq()
utility has access to your custom set of values.
import { ViewProvider } from '@greenberry/mq';
const customConfig = {
xl: 1280,
l: 1080,
m: 780,
s: 640,
xs: 560,
};
return (
<div className="app">
<ViewProvider config={customConfig}>// your code</ViewProvider>
</div>
);
If no config is provided the default will kick in, this will be named
configuration (desktop, tablet etc.). You can set this to shorthand
adding <ViewProvider shorthand></ViewProvider>
. By default the type (min or max) will be set to max
. You can manipulate this using <ViewProvider mobilefirst></ViewProvider>
Parsing values
The mq()
helper will automatically parse values unless the value can be mapped on the given config. Meaning if you use named
breakpoints (as shown above) the value will be parsed if the function recieves a number. So a custom configuration may also use strings, these strings will not be parsed. So instead of parsing 1280 (as a number) to 80rem
, a value of 1280px
will remain the same. Here is an example of using px
units within your configuration.
const customConfig = {
xl: '1280px',
l: '1080px',
m: '780px',
s: '640px',
xs: '560px',
};
Below is a simple example of the several options that can be passed as a first argument showing the parsing that will always resolve a string.
type | value | output | parse | description |
---|---|---|---|---|
string | 200px |
200px | No | Will use input |
number | 160 |
10rem | Yes | Will convert number to rem
|
string | tablet |
60rem | Yes | Will map on config and parse |
Here is a simple example of the parsing happening in the background (based on a body font-size of 16px).
mq(200, 'min')`
width: 100%;
`;
This will result in the following CSS (returned as a string).
@media screen and (min-width: 12.5rem) {
width: 100%;
}
Passing a view and view type
The first argument of the mq()
function will take either a number, a string or an array containing strings and/or numbers. Here are some examples of the output explaining what happens. It will try to automatically resolve the type either a min
, max
or a between
. The type can be set as a second argument, this is not necassary when 2 values are passed in an array, this will automatically resolve a min
and a max
value (between).
usage | value | result |
---|---|---|
mq(200)`` | number |
@media screen and (min-width: 0rem) and (max-width: 12.5rem) {} |
mq('700px')`` | string |
@media screen and (min-width: 0rem) and (max-width: 700px) {} |
mq('tablet')`` | string |
@media screen and (min-width: 0rem) and (max-width: 60rem) {} |
mq([200, 400]) | array |
@media screen and (min-width: 12.5rem) and (max-width: 25rem) {} |
mq(['760px', '1080px']) | array |
@media screen and (min-width: 760px) and (max-width: 1080px) {} |
mq(['phone, tablet']) | array |
@media screen and (min-width: 27.5rem) and (max-width: 60rem) {} |
mq([200, tablet']) | array |
@media screen and (min-width: 12.5rem) and (max-width: 60rem) {} |
Usually (for good practise) you wan to pass values in order, so first min
and then max
. However if you make a mistake mq([1080, 760])
the function will try to resolve the largest value as the max
. If you need to change the direction of the breakpoint a second argument can be passed specifing the type.
For instance mq('tablet', 'min')
, this will result in @media screen and (min-width: 60rem) {}
.
Adding additional settings
If needed some extra settings can be passed optional to specify a specific media query. By default the screen setting will automatically be set using a default. This results in @media screen and
, this can be overruled for instance to print
. Using the settings can either be passed as the first or second argument. In case of an array the type will automatically (automagically) be set to between
, meaning there is no need to be explicit about it. It prevents making a mistake.
mq('tablet', 'min', { ...settings })``;
mq(['phone', 'tablet'], { ...settings })``;
A list of settings is provided in the table below:
setting | default | result | description |
---|---|---|---|
- | screen |
@media screen and | no setting required |
{ screen: 'print' } | - | @media print and | specify key value pair |
{ orientation: true } | landscape |
(orientation: landscape) | set to true will resolve "landscape" |
{ orientation: 'portrait' } | - | (orientation: portrait) | set string will resolve string |
{ retina: true } | - | (-webkit-min-device-pixel-ratio: 2) and (min-resolution: 192dpi) | will resolve ratio and dpi |
{ mode: true } | dark |
(prefers-color-scheme: dark) | set to true will resolve "dark" |
{ mode: 'light' } | - | (prefers-color-scheme: light) | set string will resolve string |
You can combine these settings, so for instance use dark
mode on retina
only. This could for instance be initiated as follows. You could either seperate the configuration (use it multiple times) or set it inline (see second example).
const settings = {
mode: true
retina: true,
}
mq(['phone', 'tablet'], { ...settings })``;
mq(['tablet', 'desktop'], { mode: 'light' })``;
Hook
Using the useView
hook you are able to set values, nodes or other data based on a specific viewport. Behind the scenes the hook is using the same principles as the mq()
utility in combintion with the window.matchMedia
method. Doing so we are able to reuse the logic of the mq()
util.
Instead of resolving passed styles the mq()
util will only resolve the query if used as such: mq('tablet')('query')
this will return a string only containing the exact query to be used within the matchMedia
function. Meaning it would look something like this: (min-width: 0rem) and (max-width: 60rem
. The hook can be used as follows:
const [config, view] = useView(null);
view.on(['phone', 'tablet'], { name: 'Sander' });
First we set a default value, in this case we set null
. This will be the default rendering the component for the first time. Be aware that if you are trying to acces config.name
this will be empty if the default is set to null
. Using the useView
hook will return two values, the first is the state. Meaning the initial null
will be the default state for this instance.
The second value (in this example called view
) will be resposible for adding an event listener to manipulate the state (called config
in this example). This means that if the view is between phone
and tablet
the config will be changed to `{ name: 'Sander' }.
const [component, view] = useView((<div>Initial</div>));
view.on(['phone', 'tablet'], () => (<div>Example</div>)));
You could also use a callback function, this callback function makes it easy to add either logic or for instance a node/component to a specific view. In this case we call the first value to be returned component, this component
will store the component that can be changed once the view is between phone
and tablet
.
Parsing values
Parsing values will be working the same as described in the mq()
utility. To get a better understanding the following table shows some examples of the expected outcome.
usage | output |
---|---|
view.on('tablet', {}) | Will set {} between 0rem and 60rem |
view.on(200, {}) | Will set {} between 0rem and 12.5rem |
view.on(['tablet', 'desktop']) | Will set {} between 60rem and 80rem |
The on
method will manipulate the state once this specific view is true. If you want the hook to return either true or false this can be achieved not passing a second argument. This however limits the use of this hook since multiple on
events will trigger the inView
to be true. This can only be useful if one event is being use.
const [inView, view] = useView();
view.on(['phone', 'tablet']);