dereck quock

Write Media Queries Once, Use Everywhere

1 min read

Media queries are difficult to manage if you don't have a single place in your app where you have them defined. I've worked in many apps where media queries are scattered throughout CSS and components and it's nearly impossible to maintain.

I'm using emotion and I was seeing how to implement media queries on this website. Super late to the game, but I stumbled upon using facepaint to define reusable media queries.

facepaint takes an array of selectors, in our case media query breakpoints, and returns a function that you wrap around your object styles.

facepaint(selectors: Array<Selector>) : DynamicStyleFunction

Breakpoints are defined like:

import facepaint from 'facepaint';
const mq = facepaint([
'@media(min-width: 420px)',
'@media(min-width: 920px)',
'@media(min-width: 1120px)',
]);

Then you can use mq to generate media queries for a specific property:

mq({ color: ['red', 'green', 'blue'] });

💁 Remember, facepaint only works with object styles!

This generates the CSS 👇

{
"color": "red",
"@media(min-width: 420px)": {
"color": "green"
},
"@media(min-width: 920px)": {
"color": "blue"
}
}

Awesome!! 🔥 The array of colors map to the array of media query breakpoints that we passed into facepaint.

But what if we want to use facepaint with styled components? 🧐

Just use template strings!

const Block = styled.div`
${mq({ color: ['red', 'green', 'blue'] })}
`;

Now we can use our <Block> styled component with baked-in media queries and we don't have to redefine @media(min-width: 420px) all over our app. 💥 This is a massive win.

And we don't need to specify each breakpoint. We could have just used mq({ color: ['red', 'green'] }) to only cover the first breakpoint.

If you want to skip that first breakpoint and only cover the second one, you can use mq({ color: ['red', null, 'green'] }) because boolean, undefined, and null values are ignored.

A lot of the time, we need to have conditional styling based on component props. Based on the isReversed prop, we can reverse the array of colors, or the values at each breakpoint that we previously defined.

const Block = styled.div((props) => {
const { isReversed } = props;
const colors = ['red', 'green', 'blue'];
return mq({
backgroundColor: isReversed ? colors.reverse() : colors,
width: ['100%', '70%', '50%'],
height: '10vh',
margin: 'auto',
transition: 'all 0.3s ease',
});
});
<Block isReversed={true} />;

You can check out this codesandbox for yourself 👍

Edit this on CodeSandbox

Defining breakpoints and reusing media queries across apps is a lot easier with emotion and facepaint. Our apps will be easier to maintain and we can be consistent in how we support viewports of all sizes.