DEV Community

loading...
Cover image for Better Reusable Media Queries on Emotion

Better Reusable Media Queries on Emotion

Rodrigo Figueroa
Writing all the things Expo/React/Native/Web
Updated on ・3 min read

When wanting to use reusable media queries on Emotion you get in the official docs the following example:

/** @jsx jsx */
import { jsx, css } from '@emotion/core'

const breakpoints = [576, 768, 992, 1200]

const mq = breakpoints.map(
  bp => `@media (min-width: ${bp}px)`
)

render(
  <div>
    <div
      css={{
        color: 'green',
        [mq[0]]: {
          color: 'gray'
        },
        [mq[1]]: {
          color: 'hotpink'
        }
      }}
    >
      Some text!
    </div>
    <p
      css={css`
        color: green;
        ${mq[0]} {
          color: gray;
        }
        ${mq[1]} {
          color: hotpink;
        }
      `}
    >
      Some other text!
    </p>
  </div>
)
Enter fullscreen mode Exit fullscreen mode

First you set your breakpoints in an array.

const breakpoints = [576, 768, 992, 1200]
Enter fullscreen mode Exit fullscreen mode

Then you map those values to a string that will have the signature for each of your media queries. This is the piece of code that reusable media queries simplify for you.

const mq = breakpoints.map(
  bp => `@media (min-width: ${bp}px)`
)
Enter fullscreen mode Exit fullscreen mode

Finally you can easily use that media query by just using the mq variable and grabbing the index you want to use.

${mq[0]} {
 color: gray;
}
Enter fullscreen mode Exit fullscreen mode

This is fine but we can do better.

With this approach I found that when setting my media queries they didn't told me about the breakpoint being used. mq[0] is mobile, but what about mq[3]. Is it Ipad, a bigger mobile phone, or desktop?

I needed a way to be more declarative about it. Clearer as to what breakpoint am I using or needing to implement.

So long story short I leave you with the same previous example of reusable media queries but with a new implementation.

/** @jsx jsx */
import { jsx, css } from '@emotion/core'

const bp = {
  small: 500,
  large: 1200
};


const mq = n => {
  const bpArray = Object.keys(bp).map(key => [key, bp[key]]);

  const [result] = bpArray.reduce((acc, [name, size]) => {
    if (n === name) return [...acc, `@media (min-width: ${size}px)`];
    return acc;
  }, []);

  return result;
};



render(
  <div>
    <div
      css={{
        color: 'green',
        [mq('small')]: {
          color: 'gray'
        },
        [mq('large')]: {
          color: 'hotpink'
        }
      }}
    >
      Some text!
    </div>
    <p
      css={css`
        color: green;
        ${mq('small')} {
          color: gray;
        }
        ${mq('large')} {
          color: hotpink;
        }
      `}
    >
      Some other text!
    </p>
  </div>
)

Enter fullscreen mode Exit fullscreen mode

We are now defining breakpoints with an object. We can name our breakpoints with a key and then set the value. It's a plain old object that will give us way better use than the previous array.

const bp = {
  small: 500,
  large: 1200
};
Enter fullscreen mode Exit fullscreen mode

Also note that this time around we can be more flexible about the order too. It won't matter as compared with previous version where changing order in array would mess breakpoint you think you are using but you are not.

const bp = {
  small: 500,
  smaller: 300,
  large: 1200,
  iphone4: 320
};
Enter fullscreen mode Exit fullscreen mode

Next we create a method that will turn the bp object into an array of tuples with name of breakpoint and value of width pairs. Then we will reduce it to get an array with one string that has the signature for the media query you passed as the n argument just like the Emotion docs previous example did.
We finally destructure that array string value into a result variable that we will return.

const mq = n => {
  const bpArray = Object.keys(bp).map(key => [key, bp[key]]);

  const [result] = bpArray.reduce((acc, [name, size]) => {
    if (n === name) return [...acc, `@media (min-width: ${size}px)`];
    return acc;
  }, []);

  return result;
};

Enter fullscreen mode Exit fullscreen mode

So with this config we can now define and call breakpoints by name in a easy and concise way but also explicit about what we are changing.

        ${mq('small')} {
          color: gray;
        }
        ${mq('large')} {
          color: hotpink;
        }
Enter fullscreen mode Exit fullscreen mode

Discussion (6)

Collapse
dance2die profile image
Sung M. Kim

mq('small') does look more readable than mq[0] 👍
increasing the readability & maintenance cost 🙂

Collapse
valdelama profile image
Dan Winer

Can't you just do:

function mq(n) {
  return `@media (min-width: ${bp[n]}px)`;
}

?

Collapse
rmcsharry profile image
Richard McSharry

Fantastic idea, thanks for sharing it, very useful indeed!

Collapse
divyadeloitte profile image
divyadeloitte

Could you please tell react js supports below features.
Drag and drop of content block
User friendly
High performance
To implement Content management system

Collapse
nuckfuggets profile image
Joren Rothman

You can just Google that right?

Collapse
stereobooster profile image
stereobooster

It is hard to understand what exactly you ask, but I guess you want something like this react-beautiful-dnd.netlify.com/if...