Add mediaQuery helper method to manage media queries
See original GitHub issueMotivation: I really like the media query suggestions in tips-and-tricks.md, but I want something simpler to use.
In tips-and-tricks.md, we have:
// style-utils.js
import { css } from 'styled-components'
export const media = {
handheld: (...args) => css`@media (max-width: 420px) { ${ css(...args) } }`,
tablet: (...args) => css`@media (min-width: 421px) { ${ css(...args) } }`,
}
import { media } from '../style-utils';
const Box = styled.div`
font-size: 16px;
${media.handheld`
font-size: 14px;
`}
`;
The best part of this tip is that it allows the developer to pick whatever name they want when defining the static object that holds all the media query helpers (e.g. media
, respondTo
, atMedia
, …). And, since it is a static object, IDEs will offer auto-complete suggestions when you type the name of the object. I.e. typing media.
in your IDE will offer media.handheld
, media.tablet
and all other media queries defined in your object.
The only problem with the tip is that there is a lot of boilerplate. (...args) => css`@media (query goes here) { ${ css(...args) } }`,
and it is not possible (for mere mortals) to memorize that.
At first I wrote a simple helper function that setup the boilerplate and could be used like this:
const media = {
tablet: mediaHelper('(min-width: 420px)'),
}
But I realized that it would be nice if we could use a tagged template literal for the media query string too. Like this:
const media = {
tablet: mediaQuery`(min-width: 420px)`,
}
This had the bonus effect that it removed one set of parenthesis and made the code easier to read.
NOTE: if you are wondering why the mediaQuery method can’t handle the parenthesis around the query string so that you could just type this: mediaQuery`min-width: 420px`
, consider the following query: @media tv, (max-width: 520px) {}
, which is not wrapped entirely by a single set of parenthesis.
After a bit of playing with styled-component’s css() method, I came up with this:
const mediaQuery = (...query) => (...rules) => css`
@media ${css(...query)} {
${css(...rules)}
}
`
I love how this function looks. Thank you, css() method! You rock.
Now that we have that method, we can do stuff like this:
// style-utils.js
import { mediaQuery } from 'styled-components' // assuming this feature is added!
const sizes = {
tablet: 768,
desktop: 992,
giant: 1170,
}
// use em in breakpoints to work properly cross-browser and support users
// changing their browsers font-size: https://zellwk.com/blog/media-query-units/
export const media = {
handheld: mediaQuery`(max-width: ${(sizes.tablet - 1) / 16}em)`,
tablet: mediaQuery`(min-width: ${sizes.tablet / 16}em)`,
tabletOnly: mediaQuery`(min-width: ${sizes.tablet / 16}em) and (max-width: ${(sizes.desktop - 1) / 16}em)`,
desktop: mediaQuery`(min-width: ${sizes.desktop / 16}em)`,
giant: mediaQuery`(min-width: ${sizes.giant / 16}em)`,
minWidth: (pxValue) => mediaQuery`(min-width: ${pxValue / 16}em)`,
print: mediaQuery`print`,
}
import styled, { css } from 'styled-components';
import { media } from '../style-utils';
const Box = styled.div`
font-size: 16px;
${media.handheld`
font-size: 14px;
`}
/* Create a media query with a custom width not needed in other components. */
${media.minWidth(500)`
border-width: 2px;
`}
/* tagged functions created with mediaQuery can be nested with other
interpolations using styled-component's tagged function, css(). */
${({ wide }) => wide && css`
width: 100%;
${media.tablet`
width: 80%;
margin: 0 auto;
`}
`}
`;
I thought about making this a separate module ( https://github.com/JohnAlbin/styled-media-queries ), but it will be a lot more work to do that then it will to make a PR. So I’m making a PR instead. 😃 Plus, the css()
method is doing all the work.
Issue Analytics
- State:
- Created 6 years ago
- Reactions:9
- Comments:8 (6 by maintainers)
Top GitHub Comments
@philpl Apologies! That went a little over my head…
To help me understand, on May 22 you gave an example of this as a future-proof media query helper:
Can you please clarify what future-proof function should be used to generate
media.tablet
for that helper? I’d like to use a media query helper that works with the babel plugin (if that’s possible).(Thank you!)
@JohnAlbin So in the babel plugin we introduced experimental preprocessing support in v2.
Preprocessing effectively runs stylis during build time, flattening CSS, and turning it into an efficient
string[][]
structure. It does so by processing all usages ofcss
,keyframes
,injectGlobal
, and of coursestyled...
. It won’t be able to preprocess any usages of them that are purely dynamic. This means that a helper like this won’t work with the babel plugin.This is not an issue since it’s experimental right now, and not recommended to be used in production. However, the big idea is to make it stable and more tightly integrated in v3 of SC. This means that helpers like this will unexpectedly break once we do recommend the babel plugin to be used.
A future proof helper thus might look like this:
I do want to stress that your helper is perfectly fine right now and won’t break in v2. It will even work in v3 of course. But it will likely never work with the babel plugin. 😉 I hope that answers your question sufficiently