Wonderbly’s Design System Adventure

Marc Thomas
15 min readMay 15, 2017

--

Or, how we went from this…

…to this

and what we learned along the way.

A bit of background

Here at Wonderbly (formerly Lost My Name), we don’t have the biggest frontend team, and we don’t have the biggest digital design team, but we were finding that it was becoming more and more of a struggle to transparently document our rules around design, and frontend engineering. So we decided to create a Design System to help not just our teams, but the company as a whole understand and educate themselves on our design and frontend processes. It was a great learning experience for us, going from excited naivety to inherent frustration, but the end product is something we feel is truly unique in this new, dynamic space of Design Systems. And this is the story of the many bad decisions we made in order to build it.

This project actually started out around August 2015, when we first flirted with the idea of implementing a Style Guide. Both digital design and frontend went off and did some research work, figuring out what our needs would be, and seeing what else was out there. At the time our stack was Rails backend, and Rails frontend, all based upon a fork of Spree commerce, called Solidus.

Design Systems, or Style Guides, have a tendency to fall out of sync with the latest sources of truth. Maintaining two independent repositories for what should be exactly similar content is wildly inefficient. And we kept seeing Style Guides that fell foul of this, being separate entities entirely from the application they’re representative of.

This was the reason that when we first flirted with the idea of creating a Style Guide, we were shut down. “It would be a waste of designer and engineer resource” they said — rightly, to be fair. Working in such a fast-paced startup as Wonderbly, finding the time to work on a separate Style Guide would have been a very difficult task, and finding a convincing business case for that task was even more difficult. So we parked it for a few months.

The Lost My Name homepage, one of the many pages now utilising our Component Library from the Design System

Towards the end of 2015, we started investigating the merits of moving our frontend application out of Rails, and into something more appropriate. We settled on React as the natural choice, due to it’s thriving ecosystem and maturity as a framework, and began the monumental task of migrating pages of our website across, one page at a time.

We’d hit some stumbling blocks at the beginning, primarily trying to force a square peg into a round hole by trying to utilise server-side React within the Rails server — before quickly realising that an Express application run in tandem was a much saner approach.

At the same time, there was a growing need for microsites within the company, that could be quickly built and themed, usually to be a supportive asset to a blog post, or advertising campaign. We had to manually create the styles & components by hand, manually copying & pasting styles across apps to maintain visual similarity.

But of course with this method of developing, inconsistencies started to appear — fonts would be different sizes, colours slightly off, layouts not quite the same. As a result of this, we decided that all of our highly reusable components should live in a separate npm module, and each of the sites that requires them will have access to the same, standardised set of components.

The birth of the Style Guide

Our digital design team have been hard at work over the past year componentizing their Sketch files, ensuring an incredibly smooth workflow for rapidly prototyping and creating new pages. They had chosen to follow the Atomic Design principle, breaking elements up into Atoms, Molecules and Organisms. So we followed suit with our Component architecture.

Bad Idea #1 — Converting too many components

We had chosen one of the simpler pages in our product creation flow as the first page to move over from Rails to React. The page was basically just a header and footer, and a simple form determining whether a user would like their product gift wrapped. As a team we were all very new to the framework, so this made perfect sense. What didn’t make sense, however, was the fact that we decided to create components from existing Homepage and Product landing page. And converting everything, not just the reusable components from those pages. Now, in hindsight, this is obviously a monumental waste of time, but we were new to React and excited by what it could do, so we went all-guns-blazing into converting them.

The first page we chose to port over to the new stack, the Gift Wrap option page for our Lost My Name book

Also, we had no plans on our roadmap to rebuild either the Homepage or Product landing pages in React for a long, long time after we started this project, so these components that were being converted over would just sit in the bundle, bloating it’s size up. Something valuable that we did learn from this process was that abstracting out Organisms to the module felt really weird, as the Organisms would by and large be application-specific rather than consumed across our entire application suite. For example, we would only ever use the Homepage hero on the Homepage of our ecommerce site. So adding it to the module, bulking up the bundle size of every application, was just not worth it.

At the same time, the Atomic naming conventions were causing a bit of cognitive friction in our frontend team, so we decided to abandon the atomic nomenclature at an application level, reverting to a more traditional terminology of Partials and Views, replacing Organisms and Templates. The underlying metaphor remains the same, but the name changed to something more palatable — after all, we were a Rails shop in a previous life.

And so here we were, with a Component Library bustling with all the Atoms and Molecules we could think we would ever need. And around this time, with Digital Design fully committing to ‘componentizing’ their Sketch files, talk of a Style Guide emerged again.

The rebirth of the Style Guide

We all sat down for an afternoon trying to figure out whether we could make this work. In moving our reusable components over to a separate npm module, we had laid the groundwork to solve one of the most annoying problems we saw in other style guides — content becoming out of sync. If we could engineer a style guide that consumes these components, that also power our suite of applications, then we know that we’d always be in sync, therefore overcoming the biggest pain point.

From the module, we exported an object of all the Atoms and Molecules created in the Component Library, in a format like so:

import Badge from 'components/atoms/Badge';
import Button from 'components/atoms/Button';
...
import LabelledRadio from 'components/molecules/LabelledRadio';
import Panel from 'components/molecules/Panel';
export default {
atoms: {
Badge,
Button,
...
},
molecules: {
LabelledRadio,
Panel
}
};

And then when that was packaged up in npm, we could use these components in our application like this:

import LMN from '@lostmyname/styleguide';const Component = () => (
<LMN.atoms.Button
colour="primary"
href="/personalized-products">
Browse our products
</LMN.atoms.Button>
);

This seemed like a simple way of ensuring we would always have access to all of the Components in any module that consumed it, but was a very brute-force way of doing so. We could definitely do this more elegantly.

Using our code to our advantage

As well as content becoming out of date, another pain point of existing Style Guides is documentation. Obviously, documentation is very useful. Especially when it comes to components. Being able to know what props are supported, for example, is a huge benefit for a frontend engineer. We were excited by the power that React gave us with our components. After a lot of diagrams on whiteboards, we decided on an ‘ideal’ solution whereby the components themselves, and their propTypes, would power the style guide entirely, to remove any tedious, repetitive manual tasks. Anyone who has written React components knows that it gives you propType validations helpers, through the React.PropTypes object. So if there was a way of using those PropTypes to show supported props in the Style Guide, that would be an immediate benefit over other Style Guides that were out at the time.

Bad Idea #2 — putting the Style Guide web app in the same repository as the Component Library

Architecturally speaking, this makes literally no sense to anybody other than one-year-ago me when I made that decision. Whilst none of the Style Guide code appears in the bundle exported by the module, it’s still a hugely inefficient way of doing things. I think the initial rationale behind it was that the Style Guide was tightly coupled to the Component Library — each Component in the Component Library required a Component in the Style Guide — and so the cognitive load would be easier placing them in the same repository. You can see evidence of this by the module name for the exported components — @lostmyname/styleguide .

But of course that wasn’t the case. Having complementary Components for both the Component Library, and Style Guide meant that finding a file became an annoyance. I lost count of the number of times I tried to open Button.js to edit the Component, to get the Style Guide Component. Eurgh. Just… eurgh.

But we soldiered on anyway, and soon enough we had a simple little Express app up and running, and we started to build out the functionality.

A very simple — but functioning — Style Guide page, rendering a specific Component

Now, we had the Component displaying on the page, with a page title, and a description. Then we decided to put our theory to the test of having the PropTypes power the engineering-specific documentation.

After reading through the PropTypes source code in the React Github repository, we decided that it was beyond our knowledge to try and consume the native React PropType declarations in order to power our PropType table. Instead, we would write our propTypes as plain objects, and massage those objects into a correct PropType format through a helper function. The benefit of this method was that we had a very easy way of determining what the PropTypes would be for a component. The objects looked like this:

export const PropTypes = {  
colour: {
type: 'oneOf',
allowedValues: ['default', 'primary', 'secondary'],
defaultValue: 'default',
required: true,
description: 'The background colour of the button. Default maps to red on LMN, green on TJH. Primary maps to green on LMN, and green on TJH. Secondary maps to blue on both.'
},
raised: {
type: 'bool',
description: 'Whether we should add some extra styles to the button to make it appear three-dimensional, giving it a stronger appearance. Used for main CTAs.'
},
outlined: {
type: 'bool',
description: 'Whether we apply an outlined style to the button, which is the secondary button style used for secondary CTAs'
},
disabled: {
type: 'bool',
description: 'Whether the button should be disabled or not.'
},
fullWidth: {
type: 'bool',
description: 'If true, the button will span the entire width of its parent container. If false, it will default back to the width of the text, plus a bit of padding.'
},
href: {
type: 'string',
description: 'Where the button should link to. If this is left blank, the component renders as a `<button>` element rather than an `<a>` element.'
},
className: {
type: 'string',
description: 'Any extra classes to go on this component'
},
onClick: {
type: 'func',
description: 'A callback function that occurs on the click of the button'
},
children: {
type: 'any',
defaultValue: 'Click Me',
required: true,
description: 'The text on the button'
}
};

And the helper to turn this plain object notation into correct propTypes looked like this:

export const getPropTypes = function (propsObject = {}) {  
let propTypes = {};
Object.keys(propsObject).forEach(function (propName) {
let prop = propsObject[propName];
let propType;
if (prop.allowedValues) {
propType = React.PropTypes[prop.type](prop.allowedValues);
} else {
propType = React.PropTypes[prop.type];
}
if (prop.required) {
propType = propType.isRequired;
}
propTypes[propName] = propType;
});
return propTypes;
};

Bad Idea #3 — exposing Style Guide specific code to the Component Library bundle

This wasn’t a great solution, but it worked for us at the time. A consequence of writing our PropTypes as plain objects resulted in the helper functions being bundled into all our applications that consume the Component Library, when they were very specific to the Style Guide.

As you can see with the object notation, we included a description for each prop as well. This was to help power the PropType table. These descriptions could add a few kilobytes to the final bundle size, and again, are entirely Style Guide specific.

But we were blinded by the cool things we were doing making the PropType table completely powered by this PropTypes object. And after a little bit of tinkering with the code we had a functioning table, with no duplication of code whatsoever, all powered by the PropType object created for the Component.

How the first iteration of the dynamic propType table looked

Doing more with PropTypes

As we started delving deeper into our object PropTypes, we started to wonder what more we could do with them. One of our bugbears with existing Style Guides is the way that components are displayed, as a huge list of all the possible permutations all next to each other. Not only can this be a strain on cognitive load — trying to figure out what permutation of the Component you’re currently looking at — but it is a huge pain for engineers maintaining a Style Guide, having to write out all these similar components. Even more so if you document the :hover, :active, and :focus state of elements — having to write overriding CSS just for these views.

Then we had an idea. If the props of a Component entirely power the look and functionality of a Component, can we use those same PropType declarations to create controls for the Component — to make it fully editable and interactive?

Again, because of our object PropTypes we were confident it would be possible. To use our Badge component as an example, we knew we needed two text fields — one for the icon prop, and one for the children prop — and one checkbox for the absolute prop. These would have to be controlled inputs, with data being stored in a Redux store. Unfortunately for us, our knowledge of Redux still wasn’t the best, so the design of the store we ended up with wasn’t exactly ideal. A sample state looks like this:

{
textFieldValue: {
badge_icon: "heart",
badge_children: "Secure Checkout"
},
checkboxValue: {
badge_absolute: true
}
}

And as more components were added to the store, it started to look like this:

{
textFieldValue: {
badge_icon: "heart",
badge_children: "Secure Checkout",
bookLabel_children: "International Bestseller"
},
checkboxValue: {
badge_absolute: true,
bookLabel_absolute: false
},
selectValue: {
bookLabel_type: "banner"
}
}

So each input type has it’s own part of the store, and each prop per component would be ordered by the type of input. All props that are determined in the Style Guide by a <select> element, for example, would be within the selectValue part of the state.

It wasn’t clever, and it wasn’t pretty, but it worked! We had a component being displayed on a page, with documentation entirely generated by the PropTypes that it consumes, and being controlled by a set of form fields entirely generated also by the PropTypes. It was our autonomous dream!

The default view for the Badge component
And after changing the ‘Icon’ value in the controls — look how the icon in the component has updated in real time.

You can see that the Component is reactive to the changes made in the Controls, so that rather than having to manually show every possible permutation of a component in a Style Guide, we can show one, and let the user configure exactly what they want to see. We believe this could be a huge thing moving forward with Design Systems, especially as they’re coming to the forefront of people’s minds.

Pretty soon, we had a whole array of PropTypes covered, including things like React.PropTypes.oneOf, and using the values in the oneOf to auto-populate a <select> element, and some badly-implemented-but-working controls for Array and Object PropTypes.

An example of a component with a <select> element in the controls.

We were proud of where we got with this first version of the Style Guide, and it kind of worked well enough for us to stop working on it for a while, especially as we were focusing on new product launches and feature releases for the website. It was only when Digital Design started to kick on with their Sketch-based component library that we realised that we needed to rethink the Style Guide, and start to give it the attention it deserved.

The Design System

As Digital Design and Frontend started working more closely together, we realised that the Style Guide wasn’t encompassing enough to be of any real use to either team. It was heavily frontend-oriented, but lacking certain features to make it truly useful, rather than just a fun little web app.

We sat down together and reimagined the Style Guide from the ground up, and quickly realised that what we needed was more than a Style Guide. We needed a Design System, a holistic approach to inform about design and frontend across the entire company.

We dreamed up new features, such as having our design principles listed, more design-oriented guidelines, as well as the component library, which would be reimagined with more useful features for members of the company.

The new Design System homepage
The redesigned component page
A new design rule page, for our typographic rules

The new features

Design Principles and Rules

We now have sections covering these vital parts of our design process, so anybody in the company can quickly digest the information and educate themselves should they need to utilise them.

Components in iframes, and media query views

Each component is rendered in an iframe so that it gives the most accurate representation of how it behaves on certain breakpoints. We also include a media query viewer tool, which allows a user to quickly view how a component looks at our pre-defined breakpoints.

Live React code

Whenever a component is updated in the sidebar, we update a selectable JSX snippet on the page so that a non-technical person could create the component they want, and then give that snippet to an engineer to drop in to a partial.

Code snippets

When viewing any of the rules, we show what frontend code is required to achieve that rule.

Under the hood

We’ve also made some huge improvements to how the Design System was built. We revisited it from the ground up, and by having a clear architectural plan before starting development helped hugely. Also having almost 8 months more experience with the tools was a massive bonus, naturally.

The Redux store was entirely reimagined to be a lot cleaner to read, and ended up being much more saner:

{
controls: {
Button: {
colour: "primary",
raised: true,
fullWidth: true,
children: "Click Me"
}
}
}

Also, and this is a big no-brainer, but we abstracted the component library away from the web app, and also moved our build tools over from Gulp to Webpack.

And perhaps the biggest bonus from our point of view, is that every component in the new Component Library is vanilla React code, so we no longer need to export Design-System specific code into our other applications.

I hope to write more in the future about some of the architectural challenges we faced when building the Design System, but given this article is already longer than any university essay I ever wrote, I think I’ll save that for another post.

I think the biggest takeaway I had from this though, is that even though we made some pretty glaring errors when initially planning and building out the first iteration, it’s okay to make those mistakes. Because I learned a hell of a lot more going back and fixing the mistakes than I would if I never bothered at all.

But please go and take a look, it’s viewable at http://design-system.wonderbly.com, and I’d love to hear feedback from you, on both the Design System, and to this article.

--

--