How to make media queries strings in styled-components shorter and more reliable

Renas Sitdikov
Flatstack Thoughts
Published in
3 min readApr 28, 2020

--

There are some inconveniences while using CSS media queries, like starting your base design from a small screen or hard coded breakpoints.

Let’s have a look at how standard CSS media query looks like:

@media (min-width: 768px) {  // styles}

“768px” here is the breakpoint, but what if this value has to be changed and we use it multiple times? We can add CSS preprocessors like SASS to project. If you use styled-components, then Theme Provider helps to set all your custom breakpoints in one file but the code above will end up looking longer:

@media (min-width: ${({ theme: breakpoints)} => breakpoints.md) {  // styles}

But we can make this code look better, elegant and more readable. To implement it we should add these functions below into theme.js:

up: breakpoint => `@media (min-width: ${breakpoint}`,
down: breakpoint => `@media (max-width: ${breakpoint}`

These functions just return media query string depending of passed breakpoint.

And then call them inside of styled component:

const Component = styled.div(
({ theme: { up, down, breakpoints} }) => `
${down(breakpoints.md)} { // styles } ${up(breakpoints.md)} { // other styles }`,);

This code looks better, more intuitive and reliable because we use function instead of string. But it won’t work perfectly. What will happen if screen width will be exact as “md” breakpoint? Both, styles for screen lower and bigger than “md” will be used. To avoid it, we can style with “up” function only and start designing from a small screen to bigger. But this is not what we want, and we should change these functions:

up: breakpoint => `@media (min-width: calc(${breakpoint} + 0.02px))`,down: breakpoint => `@media (max-width: ${breakpoint})`,

No conflicts now! “0.02px” — is the minimal working step for modern browsers (for Safari I recommend to use “1px” instead).

So, this is how these functions with some additions look in my projects:

up: (breakpoint, vertical = false) => `@media (min-${vertical ? 'height' : 'width'}: calc(${breakpoint} + 0.02px))`,down: (breakpoint, vertical = false) => `@media (max-${vertical ? 'height' : 'width'}: ${breakpoint})`,between: (breakpointMin, breakpointMax, vertical = false) => `@media (max-${vertical ? 'height' : 'width'}: ${breakpointMax}) and (min-${vertical ? 'height' : 'width'}: calc(${breakpointMin} + 0.02px))`,

If you pass “vertical” variable as “true” it will work with “max-height’ or “min-height” and even vertical breakpoints can be used in this case.

As a summary, examples how to use all these functions:

const Wrapper = styled.div(
({ theme: { up, down, between, breakpoints} }) => `
${up(breakpoints.xs)} { // styles } // compiles to @media (min-width: 576px) {} // where 576px is the breakpoints.sm
${down(breakpoints.md)} { // styles } // compiles to @media (max-width: 768px) {} // where 768px is the breakpoints.md ${between(breakpoints.xs, breakpoints.sm)} { // styles } // compiles to @media (min-width: 576px) and (max-width: 768px) {}`,);

Those functions helped us to make responsive design in our projects easier to produce, standardized and decreased the bugs.

--

--