Demo
71 | 72 |Resize the window above by dragging the bottom right corner. The current breakpoint name is displayed in the middle of the window.
73 |├── .gitignore ├── .parcelrc ├── .prettierignore ├── .prettierrc ├── LICENSE ├── README.md ├── build-docs.js ├── build.js ├── docs ├── frame.html ├── frame.js ├── frame.scss ├── img │ ├── android-chrome-192x192.png │ ├── android-chrome-256x256.png │ ├── apple-touch-icon.png │ ├── browserconfig.xml │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── meta-image.png │ ├── mstile-150x150.png │ └── safari-pinned-tab.svg ├── index.html ├── index.js ├── index.scss ├── renderExample.js ├── site.webmanifest ├── test.html ├── test.js └── test.scss ├── package.json ├── src ├── _breakpoint-helper.scss └── breakpoint-helper.ts ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .rts2_cache_* 4 | dist 5 | example-dist 6 | docs-dist 7 | out 8 | .vscode 9 | .DS_Store 10 | .parcel-cache -------------------------------------------------------------------------------- /.parcelrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@parcel/config-default" 3 | } -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .*ignore 2 | LICENSE 3 | dist 4 | .cache 5 | docs/*.html -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5", 4 | "bracketSpacing": true 5 | } 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020-present Nicolas Cusan 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # breakpoint-helper 2 | 3 | [![npm][npm-image]][npm-url] [![license][license-image]][license-url] 4 | 5 | Small helper library (~1.1 kB) to work with layout breakpoints[1](#note-1) in Javascript. 6 | 7 | ## Demo 8 | 9 | View a demo [here](https://nicolas-cusan.github.io/breakpoint-helper/). 10 | 11 | ## Core functionality 12 | 13 | - Easily check if a breakpoint is active referencing it by name instead of value. 14 | - Listen to breakpoint changes and add/remove functionality accordingly. 15 | - Works with `px` and `em` breakpoints. 16 | - Supports `min-width` and `max-width`. 17 | - Define your own breakpoint names and values. 18 | - Share CSS breakpoints with Javascript so they only need to be maintained in one place (optional). 19 | 20 | ## Introduction 21 | 22 | In CSS it is common practice to give layout breakpoints, used in width-based media queries, names, such as `'mobile'`, `'tablet'`, `'desktop'` or `'sm'`, `'md'`, `'lg'`, to be able to easily reference them instead of having to remember exact values. 23 | 24 | Often times the the CSS breakpoints apply styling changes that need to be mirrored in Javascript, e.g. display cards in a slider on small screens (with Javascript) and as a grid on larger screens (without Javascript). 25 | 26 | **breakpoint-helper** is a thin wrapper around `window.matchMedia` that aims to make working with layout breakpoints in Javascript more convenient by allowing to reference the breakpoints by name instead of by value (`'sm'` vs. `765px`) and providing a convenient API to set and remove event listeners on media queries. 27 | 28 | ## Installation 29 | 30 | Install via [npm][npm-url] or yarn: 31 | 32 | ```shell 33 | npm install --save breakpoint-helper 34 | # or 35 | yarn add breakpoint-helper 36 | ``` 37 | 38 | ## Usage 39 | 40 | The breakpoint-helper exports a factory function to create a breakpoint-helper instance. The factory function expects to receive the breakpoints it should work with. There are different ways to provide the breakpoints, the best choice depends on the specific project setup. 41 | 42 | > **NOTE:** All initialization options expect the breakpoints to be ordered from small to large. 43 | 44 | ### Initialize with Javascript object 45 | 46 | The breakpoints can defined in an object where the object keys represent the breakpoint names and the values the screen widths. The values should be of type `string` and include a CSS unit, both `px` and `em` are supported. 47 | 48 | ```js 49 | import breakpointHelper from 'breakpoint-helper'; 50 | 51 | const bph = breakpointHelper({ 52 | xs: '416px', 53 | sm: '600px', 54 | md: '768px', 55 | lg: '1024px', 56 | xl: '1280px', 57 | xxl: '1520px', 58 | }); 59 | 60 | export default bph; 61 | ``` 62 | 63 | ### Initialize using CSS custom properties 64 | 65 | Declare custom properties on the `:root` selector using the prefix `--bph-`: 66 | 67 | ```css 68 | :root { 69 | --bph-xs: 416px; 70 | --bph-sm: 600px; 71 | --bph-md: 768px; 72 | --bph-lg: 1024px; 73 | --bph-xl: 1280px; 74 | --bph-xxl: 1520px; 75 | } 76 | ``` 77 | 78 | Then initialize breakpoint-helper passing the string `'custom'` as an argument: 79 | 80 | ```js 81 | // src/utils/bph.js 82 | import breakpointHelper from 'breakpoint-helper'; 83 | 84 | const bph = breakpointHelper('custom'); 85 | 86 | export default bph; 87 | ``` 88 | 89 | ### Initialize with Sass map (share CSS breakpoints with Javascript) 90 | 91 | Breakpoint-helper provides a sass mixin that allows the use of a Sass map to define the breakpoints. To use this method use the mixin in your Sass code by passing it the breakpoints as argument: 92 | 93 | ```scss 94 | // Import the mixin, path may vary depending on implementation 95 | @import './node_modules/breakpoint-helper/src/breakpoint-helper'; 96 | 97 | // Define a map of breakpoints 98 | $bps: ( 99 | 'xs': 416px, 100 | 'sm': 600px, 101 | 'md': 768px, 102 | 'lg': 1024px, 103 | 'xl': 1280px, 104 | 'xxl': 1520px, 105 | ); 106 | 107 | // Use the mixin 108 | @include breakpoint-helper($bps); 109 | ``` 110 | 111 | Then initialize breakpoint-helper with the string `'meta'` as argument: 112 | 113 | ```js 114 | // src/utils/bph.js 115 | import breakpointHelper from 'breakpoint-helper'; 116 | 117 | const bph = breakpointHelper('meta'); 118 | 119 | export default bph; 120 | ``` 121 | 122 | #### What is happening here? 123 | 124 | The Sass mixin will create a ruleset for the class `.breakpoint-helper` with a single `font-family` declaration, the `font-family` value will be a serialized string of the breakpoint map: 125 | 126 | ```css 127 | .breakpoint-helper { 128 | font-family: 'xs=416px&sm=600px&md=768px&lg=1024px&xl=1280px&xxl=1520px'; 129 | } 130 | ``` 131 | 132 | When breakpoint-helper gets initialized it will create a `` element in the document's `
` tag with the class `breakpoint-helper`, read the `font-famliy` CSS value and deserialize it. 133 | 134 | > **NOTE:** This method does not require the use of Sass or the mixin per se. All that is required is the class `.breakpoint-helper` with the serialized breakpoints as `font-family` value. 135 | 136 | ### Typescript 137 | 138 | As of `v2.0.0` The library is written in Typescript and types definitions are available. 139 | 140 | ### Usage with React 141 | 142 | To use breakpoint-helper in React, setup an instance using any of the methods above and use it within a `useEffect` hook. You can then use a `useState` hook to use it inside your component's render function. 143 | 144 | ```jsx 145 | import { useEffect, useState } from 'react'; 146 | import { listen } from './src/utils/bph'; 147 | 148 | const MyComponent = (props) => { 149 | const [isMatching, setIsMatching] = useState(true); // Set a default value in case you are using SSR 150 | 151 | useEffect(() => { 152 | const listener = listen('sm', ({ matches }) => { 153 | setIsMatching(matches); 154 | }); 155 | return () => listener.off(); // Remove the event listener on component unmount 156 | }, []); 157 | 158 | return isMatching ? ( 159 |Small helper library to work with layout breakpoints1 in Javascript.
45 |Resize the window above by dragging the bottom right corner. The current breakpoint name is displayed in the middle of the window.
73 |px
and em
breakpoints.min-width
and max-width
.In CSS it is common practice to give layout breakpoints, used in width-based media queries, names, such as 'mobile'
, 'tablet'
, 'desktop'
or 'sm'
, 'md'
, 'lg'
, to be able to easily reference them instead of having to remember exact values.
Often times the the CSS breakpoints apply styling changes that need to be mirrored in Javascript, e.g. display cards in a slider on small screens (with Javascript) and as a grid on larger screens (without Javascript).
92 |breakpoint-helper is a thin wrapper around window.matchMedia
that aims to make working with layout breakpoints in Javascript more convenient by allowing to reference the breakpoints by name instead of by value ('sm'
vs. 765px
) and providing a convenient API to set and remove event listeners on media queries.
Install via npm or yarn:
95 |npm install --save breakpoint-helper
96 | # or
97 | yarn add breakpoint-helper
98 |
The breakpoint-helper exports a factory function to create a breakpoint-helper instance. The factory function expects to receive the breakpoints it should work with. There are different ways to provide the breakpoints, the best choice depends on the specific project setup.
100 |101 |103 |NOTE: All initialization options expect the breakpoints to be ordered from small to large.
102 |
The breakpoints can defined in an object where the object keys represent the breakpoint names and the values the screen widths. The values should be of type string
and include a CSS unit, both px
and em
are supported.
import breakpointHelper from 'breakpoint-helper';
106 |
107 | const bph = breakpointHelper({
108 | xs: '416px',
109 | sm: '600px',
110 | md: '768px',
111 | lg: '1024px',
112 | xl: '1280px',
113 | xxl: '1520px',
114 | });
115 |
116 | export default bph;
117 |
Declare custom properties on the :root
selector using the prefix --bph-
:
:root {
120 | --bph-xs: 416px;
121 | --bph-sm: 600px;
122 | --bph-md: 768px;
123 | --bph-lg: 1024px;
124 | --bph-xl: 1280px;
125 | --bph-xxl: 1520px;
126 | }
127 |
Then initialize breakpoint-helper passing the string 'custom'
as an argument:
// src/utils/bph.js
129 | import breakpointHelper from 'breakpoint-helper';
130 |
131 | const bph = breakpointHelper('custom');
132 |
133 | export default bph;
134 |
Breakpoint-helper provides a sass mixin that allows the use of a Sass map to define the breakpoints. To use this method use the mixin in your Sass code by passing it the breakpoints as argument:
136 |// Import the mixin, path may vary depending on implementation
137 | @import './node_modules/breakpoint-helper/src/breakpoint-helper';
138 |
139 | // Define a map of breakpoints
140 | $bps: (
141 | 'xs': 416px,
142 | 'sm': 600px,
143 | 'md': 768px,
144 | 'lg': 1024px,
145 | 'xl': 1280px,
146 | 'xxl': 1520px,
147 | );
148 |
149 | // Use the mixin
150 | @include breakpoint-helper($bps);
151 |
Then initialize breakpoint-helper with the string 'meta'
as argument:
// src/utils/bph.js
153 | import breakpointHelper from 'breakpoint-helper';
154 |
155 | const bph = breakpointHelper('meta');
156 |
157 | export default bph;
158 |
The Sass mixin will create a ruleset for the class .breakpoint-helper
with a single font-family
declaration, the font-family
value will be a serialized string of the breakpoint map:
.breakpoint-helper {
161 | font-family: 'xs=416px&sm=600px&md=768px&lg=1024px&xl=1280px&xxl=1520px';
162 | }
163 |
When breakpoint-helper gets initialized it will create a <meta>
element in the document's <head>
tag with the class breakpoint-helper
, read the font-famliy
CSS value and deserialize it.
165 |167 |NOTE: This method does not require the use of Sass or the mixin per se. All that is required is the class
166 |.breakpoint-helper
with the serialized breakpoints asfont-family
value.
As of v2.0.0
The library is written in Typescript and types definitions are available.
To use breakpoint-helper in React, setup an instance using any of the methods above and use it within a useEffect
hook. You can then use a useState
hook to use it inside your component's render function.
import { useEffect, useState } from 'react';
172 | import { listen } from './src/utils/bph';
173 |
174 | const MyComponent = (props) => {
175 | const [isMatching, setIsMatching] = useState(true); // Set a default value in case you are using SSR
176 |
177 | useEffect(() => {
178 | const listener = listen('sm', ({ matches }) => {
179 | setIsMatching(matches);
180 | });
181 | return () => listener.off(); // Remove the event listener on component unmount
182 | }, []);
183 |
184 | return isMatching ? (
185 | <div className="is-matching">Matching the "sm" breakpoint.</div>
186 | ) : (
187 | <div className="not-matching">Not matching the "sm" breakpoint.</div>
188 | );
189 | };
190 |
Each breakpoint-helper instance returns methods to work with the breakpoints.
192 |In larger projects it is convenient to create a reusable breakpoint-helper instance module and export the returned methods for easier usage.
193 |import breakpointHelper from 'breakpoint-helper';
194 |
195 | // Could be any other of the initialization methods
196 | const instance = breakpointHelper({
197 | xs: '416px',
198 | sm: '600px',
199 | md: '768px',
200 | lg: '1024px',
201 | xl: '1280px',
202 | xxl: '1520px',
203 | });
204 |
205 | export const {
206 | getBreakpoints,
207 | getMediaQuery,
208 | isMatching,
209 | listen,
210 | listenAll,
211 | } = instance; // Destructure methods and export for conveninece
212 |
213 | export default instance;
214 |
215 |217 |NOTE: The following code examples assume the use of the instance above.
216 |
getBreakpoints()
Get all breakpoints as an object. Useful for debugging or passing breakpoint values to other libraries.
219 |Object
: Object containing all instance breakpoints.import { getBreakpoints } from './src/utils/bph';
225 |
226 | const breakpoints = getBreakpoints();
227 |
228 | console.log(breakpoints);
229 | // {
230 | // xs: '416px',
231 | // sm: '600px',
232 | // md: '768px',
233 | // lg: '1024px',
234 | // xl: '1280px',
235 | // xxl: '1520px',
236 | // }
237 |
getMediaQuery(name, [useMax=false])
Get a min-width
, max-width
or min-width and max-width
media query by breakpoint name.
name
{string|Array}
: Breakpoint name or array of two breakpoint names.2[useMax=false]
{boolean}
: Use max-width
instead of min-width
3.{string}
: Media query string.import { getMediaquery } from './src/utils/bph';
250 |
251 | const mq = getMediaquery('md');
252 | console.log(mq);
253 | // '(min-width: 768px)'
254 |
255 | const mqMax = getMediaquery('md', true);
256 | console.log(mqMax);
257 | // '(max-width: 767px)'
258 |
259 | const mqMinMax = getMediaquery(['md', 'lg']);
260 | console.log(mqMax);
261 | // '(min-width: 768px) and (max-width: 1023px)'
262 |
isMatching(name, [useMax=false])
Check if a breakpoint or breakpoint range is currently matching.
264 |name
{string|Array}
: Breakpoint name or array of two breakpoint names.2[useMax=false]
{boolean}
: Use max-width
instead of min-width
3.{boolean}
: Whether the breakpoint or breakpoint range is matching or not.import { isMatching } from './src/utils/bph';
275 |
276 | if (isMatching('md')) {
277 | // Do something
278 | } else {
279 | // Do something else
280 | }
281 |
282 | if (isMatching(['md', 'lg'])) {
283 | // Screen width is between 'md' and 'lg'
284 | }
285 |
listen(options, callback)
Listen to a breakpoint or breakpoint range change and execute a callback function. The callback function will receive a MediaQueryList
object as parameter that can be used to check wether the breakpoint media query is matching or not. The callback function is called once on listener creation, it is possible to opt out of this behavior via options.
options
{Object|string|Array}
Configuration Object, breakpoint name or array of two breakpoint names.options.name
{string}
: Breakpoint name or array of two breakpoint names.2[options.useMax=false]
{boolean}
: Use max-width
instead of min-width
3.[options.immediate=true]
{boolean}
: Execute callback function on listener creation.callback
{Function}
: Callback function, receives a MediaQueryList
as parameter.{Object}
: Object containing the on
and off
listener methods.import { listen } from './src/utils/bph';
301 |
302 | // Destructure the `MediaQueryList.matches` property
303 | const callback = ({ matches }) => {
304 | if (matches) {
305 | // Do somthing
306 | } else {
307 | // Do somthing else
308 | }
309 | };
310 |
311 | const listener = listen('md', callback);
312 |
313 | // Remove the event listener
314 | listener.off();
315 |
316 | // Activate it again
317 | listener.on();
318 |
Using an options object instead of a breakpoint name:
319 |import { listen } from './src/utils/bph';
320 |
321 | const listener = listen(
322 | {
323 | name: 'md',
324 | useMax: true,
325 | immediate: false,
326 | },
327 | callback
328 | );
329 |
330 | const callback = ({ matches }) => {
331 | // ...
332 | };
333 |
listenAll(callback, [options])
Listen to all breakpoints matching or un-matching and execute a callback function. The callback function will receive an array of the matching breakpoint names in reverse order as a parameter. That means the largest breakpoint name (or smallest when using options.useMax
) comes first in the array. The array will be empty if no breakpoints are matching.
callback
{Function}
: Callback function, receives an array of breakpoint names as parameter.[options]
{Object}
: Configuration Object.[options.listenTo]
{Array}
: Array of breakpoint names. All are used by default.[options.useMax=false]
{boolean}
: Use max-width
instead of min-width
3.[options.immediate=true]
{boolean}
: Execute callback function on listener creation.{Object}
: Object containing the on
and off
listener methods.import { listenAll } from './src/utils/bph';
349 |
350 | const listener = listenAll(callback);
351 |
352 | const callback = (bps) => {
353 | // Get the first breakpoint in the `bps` array.
354 | const match = bps[0];
355 |
356 | // If the largest matching breakpoint is 'lg', it will
357 | // be the first in the array `['lg', 'md', 'sm', 'xs',]`.
358 |
359 | switch (match) {
360 | case 'lg':
361 | // Do something if the breakpoint is 'lg'
362 | break;
363 | case 'md':
364 | // Do something if the breakpoint is 'md'
365 | break;
366 | case 'sm':
367 | // Do something if the breakpoint is 'sm'
368 | break;
369 |
370 | default:
371 | // Do something if another breakpoint is matching or none is
372 | break;
373 | }
374 | };
375 |
376 | // Remove the event listener
377 | listener.off();
378 |
379 | // Activate it again
380 | listener.on();
381 |
Limit the breakpoints by passing using options.listenTo
:
import { listenAll } from './src/utils/bph';
383 |
384 | const listener = listenAll(callback, {
385 | // Only listen to the breakpoints 'xl', 'lg' and 'sm'.
386 | listenTo: ['xl', 'lg', 'sm'],
387 | // Use `max-width` media queries instead of `min-width`
388 | useMax: true,
389 | });
390 |
391 | const callback = (bps) => {
392 | // ...
393 | };
394 |
Depending on the bundle the library is about 1.1 kB when gziped.
396 |The library is compatible with modern browsers and should also work with IE11 (not tested).
397 |I kept needing something like this and was copying the same script from project to project, so I decided to open-source it. On one hand it might help somebody that has the same need as I do and on the other it allows me to install just as another dependency.
405 |The initialization via metatag and the serialized font-family
value is taken from Zurb's Foundation.
1) Browser window widths at which styles/functionality changes to adapt for wider/narrower screens.
409 |2) The useMax
argument will be ignored when name
is an array.
3) When using useMax
breakpoint-helper will subtract 1px
from the breakpoint value to prevent overlap. If the breakpoint value is defined in em
s 0.0635em
is subtracted (the equivalent of 1px
in em
using a 16px
base).
${title}
${subtitle}`;
50 | codePre.innerHTML = `${code}
`;
51 | resultPre.innerHTML = `${result}
`;
52 |
53 | if (title) {
54 | root.appendChild(h2);
55 | }
56 |
57 | if (subtitle) {
58 | h2.innerHTML = subtitle;
59 | root.appendChild(h2);
60 | }
61 |
62 | if (code || result) {
63 | root.appendChild(example);
64 | }
65 |
66 | if (useBtn) {
67 | button.innerHTML = 'Disable';
68 | exampleCode.appendChild(button);
69 | codePre.classList.add('example_pre-button');
70 | }
71 |
72 | let isActive = true;
73 |
74 | return {
75 | code: (txt) => {
76 | codePre.innerHTML = `${txt}
`;
77 | },
78 | result: (txt) => {
79 | resultPre.innerHTML = `${txt}
`;
80 | },
81 | button: (listener = null) => {
82 | if (!listener) return;
83 |
84 | button.addEventListener('click', () => {
85 | if (isActive) {
86 | listener.off();
87 | resultPre.innerHTML = `// listener.off() was called!\n// The listener is currently disabled
`;
88 | button.innerHTML = 'Enable';
89 | resultPre.classList.toggle('example_pre-disabled');
90 | } else {
91 | resultPre.innerHTML = `${result}`;
92 | button.innerHTML = 'Disable';
93 | resultPre.classList.toggle('example_pre-disabled');
94 | listener.on();
95 | }
96 |
97 | isActive = !isActive;
98 | });
99 | },
100 | };
101 | }
102 |
--------------------------------------------------------------------------------
/docs/site.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name": "BPH",
3 | "short_name": "BPH",
4 | "icons": [
5 | {
6 | "src": "./img/android-chrome-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | },
10 | {
11 | "src": "./img/android-chrome-256x256.png",
12 | "sizes": "256x256",
13 | "type": "image/png"
14 | }
15 | ],
16 | "theme_color": "#ffffff",
17 | "background_color": "#ffffff",
18 | "display": "standalone"
19 | }
20 |
--------------------------------------------------------------------------------
/docs/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |