NOTE: This blog is targeted at javascript developers with experience using React, but many of the design-system concepts should be applicable across any User Interface (UI).
The goal of this blog is to talk about:
April 2018 I began my work at Oqton as a front-end engineer. Like many engineers working at startups, I came onboard with the understanding that my job would involve writing code, but the specifics would change as the company’s goals evolved. Before working at Oqton, my experience with css was very minimal. I generally felt okay modifying existing css, but I would hesitate if you were to ask me to implement any large feature to match a design specification.
At some point during the first week, I noticed a bug in the UI that was only happening on Firefox. I did a bit of googling, wrote a few lines of css, and was very proud of myself when I was able to make a PR that addressed the issue. As the app evolved, our UI-Lead (who had been doing most of the css to this point) had assigned me a few more css-related tickets. I was frustrated by how long it took me to implement designs that, on first-glance, I considered “so simple”.
While googling furiously to find the solution to a css-problem I was stuck on, I came upon the site Flexbox-Zombies. They didn’t have an immediate solution to my problem, but they did completely change the way that I approach css. This site offers an amazing (free) course that teaches you step-by-step how to use flexbox. I can’t say this enough:
Flexbox-Zombies has been one of the most useful learning resources in my career and I recommend it to anyone who uses css.
After completing the flexbox-zombies course, I became more confident in fixing css and even implementing new features.
Our UI-Lead had mentioned that we were potentially looking to hire another UI engineer. He had mentioned that the candidate would either be focusing on business-logic or design-system implementation. By this time I had cut my teeth on a few more css-related tickets. I wondered if I had the skills necessary to implement the styling for our app. I offered that I would be interested in taking ownership of our style implementation and beginning the work in making an official design system for our UI team.
We utilize:
When array syntax goes wrong
Most boilerplate Styled System examples utilize array syntax like this
const theme = {
space: [
0,
8,
16
],
};
The problem for our team was that the design-spec was in flux and we wanted to make sure if an additional space, fontSize or breakpoint was necessary, it could be added without having to overwrite all existing style declarations. Note, the theme
is just an object, so you can always create aliases in addition to using an array, but in this example we’ll focus on the benefits we found of using object-keys over array-index shorthand.
For example, if we had a <Box />
component that used the styled-system space-prop for padding:
<Box pt={0} />
<Box pt={1} />
<Box pt={2} />
But later on we wanted to add a spacing value of 4, for some reason.
const theme = {
space: [
0,
4,
8,
16
],
};
We would have to rewrite every instance of the <Box />
component to
<Box pt={0} />
<Box pt={2} />
<Box pt={3} />
Object-key-syntax to save the day
By using key-based spacing, we are able to add additional values (relatively) painlessly
const theme = {
space: {
none: 0,
s: 8,
m: 16
},
};
And then consuming our theme will look like this:
<Box pt="none" />
<Box pt="s" />
<Box pt="m" />
If after the fact we want to add a spacing value of { xs: 4 }
we can do so without having to rewrite any existing style declarations.
This same pattern can be applied with any styled-system helper function
Styled System allows for modifying space props based on the current breakpoint. For example with key-based breakpoints for the theme
const theme = {
space: {
none: 0,
xs: 4,
s: 8,
m: 16
},
breakpoints: {
s: 0,
m: 400,
l: 800,
xl: 1200,
}
};
You can use breakpoint keys to set per-breakpoint padding
const paddingTop = {
s: 'xs',
m: 's',
l: 's',
xl: 'm'
};
<Box pt={paddingTop} />
Note, since breakpoints set a min-width
media query, it’s not necessary to set multiple breakpoint values. For example the above code snippet can be condensed to this:
const paddingTop = {
s: 'xs',
m: 's',
xl: 'm'
};
<Box pt={paddingTop} />
Managing multiple responsive components in the context of a layout can become a bit messy. Imagine a layout that behaves like this:
Large Breakpoint | Small Breakpoint |
---|---|
Instead of using media queries and per-breakpoint style declaration, in many cases it was easier to create a per-breakpoint layout. We set event listeners onResize and then determine the responsiveKey
from that.
The core concept of this code is that we hold the “current” responsive breakpoint as state in react and then pass down that state, either via props or context to the rest of the app.
Here’s a basic example of the logic behind setting the responsiveKey
state in react:
import React, { useEffect, useState } from "react";
import { LargeLayout } from './LargeLayout';
import { SmallLayout } from './SmallLayout';
const theme = {
breakpoints: {
s: 0,
l: 600,
}
};
function ResponsiveLayout(props) {
const [responsiveKey, setResponsiveKey] = useState(getCurrentResponsiveKey());
function getCurrentResponsiveKey() {
// Fall-back to the smallest breakpoint if nothing else matches
let currentResponsiveKey = Object.keys(theme.breakpoints)[0];
for(const [key, value] of Object.entries(theme.breakpoints)) {
if (window.innerWidth >= value) {
currentResponsiveKey = key;
}
}
return currentResponsiveKey;
}
// componentDidMount
useEffect(() => {
function setCurrentResponsiveKey() {
const newResponsiveKey = getCurrentResponsiveKey();
setResponsiveKey(newResponsiveKey);
}
window.addEventListener('resize', setCurrentResponsiveKey);
// componentWillUnmount
return () => {
window.removeEventListener('resize', setCurrentResponsiveKey);
}
}, []);
if(responsiveKey === 's') {
return <SmallLayout {...props} />
}
return <LargeLayout {...props} />;
}
Expanding on this idea, here’s a full fledged implementation using styled-components and styled-system:
Here is an example of this layout.
Here’s the code.
That’s a brief look into some ideas we’ve been exploring when implementing our design system.
Thanks for reading