Storybook & Atomic Design – 1.15. – Responsive Styled Components

In this lesson, we'll be looking at how we can create helpful SCSS mixin inspired JavaScript references to use throughout our component styles.

So far we've been using the old-school method of writing media queries by specifically setting a hard coded breakpoint min-width value to apply our CSS rules at.

If we take a look at the header component specifically we'll see this with the following styles -

export const StyledHeader = styled.header`
	align-items: center;
	display: flex;
	left: ${props => (props.variant ? `0` : undefined)};
	padding: 30px;
	position: relative;
	top: ${props => (props.variant ? `0` : undefined)};
	width: 100%;
	z-index: 9;

	color: ${props => headerColour(props)};

	@media (min-width: 992px) {
		display: block;
		padding: 0;
		position: ${props => headerPosition(props)};
	}

	button {
		display: inline-flex;
		margin-left: auto;

		@media (min-width: 992px) {
			display: none;
		}
	}

	img {
		height: 40px;

		@media (min-width: 992px) {
			height: 64px;
			left: 50%;
			position: absolute;
			top: 50%;

			transform: translate(-50%, -50%);
		}
	}
`;

While this CSS is valid and there's nothing wrong with using the code snippet found above, we can improve it by using variable values for our breakpoints.

Advantages of a Global Media Query File

The advantage of setting up a global file that stores our breakpoints in the form of integers, px strings, and media query strings is that we can create a single source of truth for our media queries. Again allowing us to modify these breakpoints in the future by updating one source, instead of searching every reference to a media query and replacing the values manually.

Instead of defining the media queries with a value like -

@media (min-width: 992px) {

We'll instead be able to reference a global breakpoint value using -

@media ${device.md} {

Where device.md will be a value of (min-width: 992px).

mediaQueries.jsx

To implement our responsive breakpoint values, we'll need a file to store the information in. Doing so will allow us to import these values to any of our styled-components or reference the breakpoints in other component settings (e.g. Slick slider).

As the media query values aren't visual styles or elements, and instead data, we will store the mediaQueries.jsx file in our particles directory.

- __Storybook__
   - __components__
     - __particles__
       - mediaQueries.jsx

Our mediaQueries.jsx file will contain the following -

export const breakpoints = {
	xs: 576,
	sm: 768,
	md: 992,
	lg: 1200,
	xl: 1440,
	xxl: 1800
};

export const size = {
	xs: `${breakpoints.xs}px`,
	sm: `${breakpoints.sm}px`,
	md: `${breakpoints.md}px`,
	lg: `${breakpoints.lg}px`,
	xl: `${breakpoints.xl}px`,
	xxl: `${breakpoints.xxl}px`
};

export const device = {
	xs: `(min-width: ${size.xs})`,
	sm: `(min-width: ${size.sm})`,
	md: `(min-width: ${size.md})`,
	lg: `(min-width: ${size.lg})`,
	xl: `(min-width: ${size.xl})`,
	xxl: `(min-width: ${size.xxl})`
};

What we're doing in this file is defining our breakpoints (xs - xxl) which are heavily inspired by Bootstrap 4's breakpoints.

Next, we set up a new object for size which allows us to use the integer values defined in breakpoints within the context of a px valued string.

Finally, we create the device object which interpolates the size values to exist in a media query string. Allowing us to import device from mediaQueries.jsx in our Styled-Component and reference the object values to create a media query.

Using our Media Queries

Revisiting our Header component, we can now import the device value and use it in our styles to define media queried styles.

At the top of the header.styles.jsx file, we'll need to import the value from mediaQueries.jsx

import styled from "styled-components";
import { device } from "../../particles/mediaQueries";

Next, we can replace the previous references to our hardcoded media queries -

export const StyledHeader = styled.header`
	align-items: center;
	display: flex;
	left: ${props => (props.variant ? `0` : undefined)};
	padding: 30px;
	position: relative;
	top: ${props => (props.variant ? `0` : undefined)};
	width: 100%;
	z-index: 9;

	color: ${props => headerColour(props)};

	@media ${device.md} {
		display: block;
		padding: 0;
		position: ${props => headerPosition(props)};
	}

	button {
		display: inline-flex;
		margin-left: auto;

		@media ${device.md} {
			display: none;
		}
	}

	img {
		height: 40px;

		@media ${device.md} {
			height: 64px;
			left: 50%;
			position: absolute;
			top: 50%;

			transform: translate(-50%, -50%);
		}
	}
}
`;

Now we have a flexible and consistent set of breakpoint values that we can use to create responsive Styled-Components in our pattern library.

If you have any components which aren't mobile-friendly and optimised across devices, please do feel free to implement the new media query logic and improve your CSS for the existing React components in your system.

In the next lesson, we'll be deploying our Storybook environment to Netlify for free, allowing anyone interested in our components to view them without the need to download and set up the project locally.

Continue Reading 📚