Skip to content

Instantly share code, notes, and snippets.

@Jayphen
Last active February 13, 2018 08:53
Show Gist options
  • Save Jayphen/07a035b21d6670eac3a9cee6d77d2fc4 to your computer and use it in GitHub Desktop.
Save Jayphen/07a035b21d6670eac3a9cee6d77d2fc4 to your computer and use it in GitHub Desktop.
A wrapper around react-responsive to simplify breakpoint use
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import MediaQuery from 'react-responsive';
import {
labels,
getBreakpointValue,
getMaxBreakpointValue,
getNextBreakpoint
} from '../../util/breakpoints';
let Above, Below, Between, Only;
// Above
// @media (min-width: 576px)
// <Above breakpoint="sm" />
// Below
// @media (max-width: 575.98px)
// <Below breakpoint="sm" />
// Only
// @media (min-width: 0) and (max-width: 575.98px)
// <Only breakpoint="xs" />
// Between
// @media (min-width: 768px) and (max-width: 1199.98px)
// <Between breakpoints={["sm", "lg"]} />
/** ---------------------------- */
Above = ({ breakpoint, children }) => (
<MediaQuery minWidth={getBreakpointValue(breakpoint)}>
<Fragment>{children}</Fragment>
</MediaQuery>
);
Above.propTypes = {
breakpoint: PropTypes.oneOf(labels).isRequired
};
/** ---------------------------- */
Below = ({ breakpoint, children }) => (
<MediaQuery maxWidth={getMaxBreakpointValue(breakpoint)}>
<Fragment>{children}</Fragment>
</MediaQuery>
);
Below.propTypes = {
/** Any breakpoint label except XS */
breakpoint: PropTypes.oneOf(labels.filter(label => label !== 'xs')).isRequired
};
/** ---------------------------- */
Only = ({ breakpoint, children }) => (
<MediaQuery
minWidth={getBreakpointValue(breakpoint)}
maxWidth={getMaxBreakpointValue(getNextBreakpoint(breakpoint))}
>
{children}
</MediaQuery>
);
Only.propTypes = {
breakpoint: PropTypes.oneOf(labels).isRequired
};
/** ---------------------------- */
Between = ({ breakpoints, children }) => (
<MediaQuery
minWidth={getBreakpointValue(breakpoints[0])}
maxWidth={getMaxBreakpointValue(breakpoints[1])}
>
{children}
</MediaQuery>
);
Between.propTypes = {
/** Accepts an array of 2 labels – e.g [xs, sm] */
breakpoints: PropTypes.arrayOf(PropTypes.oneOf(labels)).isRequired
};
/** ---------------------------- */
export { Above, Below, Only, Between };
const findBreakpoint = (label, breakpoints) =>
breakpoints.find(bp => bp.label === label);
const getBreakpointValue = label =>
typeof label === 'object'
? label.size
: findBreakpoint(label, breakpoints).size;
const getMaxBreakpointValue = label => {
if (label === null) return;
/**
* We do this to prevent collions between breakpoints.
* https://www.w3.org/TR/mediaqueries-4/#range-context
*/
const breakpointValue = getBreakpointValue(label).toString();
const postfix = breakpointValue.match(/[a-zA-Z]+/) || '';
const value = breakpointValue.match(/[0-9]+/);
return `${value - 0.01}${postfix}`;
};
function getNextBreakpoint(breakpoint) {
let index = breakpoints.indexOf(findBreakpoint(breakpoint, breakpoints));
return index !== breakpoints.length - 1 ? breakpoints[index + 1] : null;
}
export {
labels,
findBreakpoint,
getBreakpointValue,
getMaxBreakpointValue,
getNextBreakpoint
};
import palette from './colors';
import { getMaxBreakpointValue, getNextBreakpoint } from '../util/breakpoints';
const breakpoints = [0, '36em', '48em', '62em', '75em'];
const labels = ['xs', 'sm', 'md', 'lg', 'xl'];
const breakpointMap = breakpoints.reduce((arr, size, index) => {
return [
...arr,
{
label: labels[index],
size: size
}
];
}, []);
const space = [0, 8, 16, 32, 64, 96];
const fontSizes = [10, 12, 14, 16, 20, 24, 28, 36, 48, 64, 72];
const fontWeights = [300, 500];
const lineHeights = [1.5, 1.75];
const colors = {
darkGray: palette.gray[9],
mediumGray: palette.gray[5],
lightGray: palette.gray[4],
lighterGray: palette.gray[1],
lightestGray: palette.gray[0],
...palette
};
const above = breakpointMap.reduce((obj, bp) => {
return {
...obj,
[bp.label]: `@media (min-width: ${bp.size})`
};
}, {});
const below = breakpointMap.reduce((obj, bp) => {
return {
...obj,
[bp.label]: `@media (max-width: ${getMaxBreakpointValue(bp.label)})`
};
}, {});
const between = breakpointMap.reduce((obj, bp, breakpointMapIndex) => {
/**
* Create an array of min - max labels for each breakpoint
* (xs-md, xs-lg etc)
*/
const breakpointLabels = labels
.reduce((arr, label, breakpointLabelIndex) => {
return [
...arr,
bp.label === label
? null
: breakpointMapIndex < breakpointLabelIndex
? { name: `${bp.label}-${label}`, from: bp.label, to: label }
: null
];
}, [])
.filter(bp => bp !== null);
/**
* Create an array of CSS media queries from the breakpoint labels
*/
const mediaQueries = breakpointLabels.reduce((obj, bpName) => {
return {
...obj,
[bpName.name]: `@media (min-width: ${bp.size}) and (max-width: ${
breakpointMap.find(bp => bp.label === bpName.to).size
})`
};
}, {});
return {
...obj,
...mediaQueries
};
}, {});
const only = breakpointMap.reduce((obj, bp) => {
let nextBreakpoint = getNextBreakpoint(bp.label);
let nextSize = nextBreakpoint
? getMaxBreakpointValue(nextBreakpoint.label)
: null;
return {
...obj,
/**
* Create min-max queries to target the specific size requested.
* The last size in the array has no upper limit, so acts the same as
* `from`
*/
[bp.label]: nextSize
? `@media (min-width: ${bp.size}) and (max-width: ${nextSize})`
: `@media (min-width: ${bp.size})`
};
}, {});
export default {
breakpoints,
breakpointMap,
labels,
space,
fontSizes,
colors,
lineHeights,
fontWeights,
above,
below,
between,
only
};
export { breakpointMap, labels };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment