How I led the design effort to built StartupJS: A design system that allowed my agency to build digital products for 19 different enterprise and startup clients.
Multiple (19 Products Built)
For the last 8 years, I've led the product design team at DecisionMapper, a digital agency that builds digital products for enterprise and startup clients.
During that time, we built most products using design systems. This approach allowed us to iterate faster and achieve more consistent results, but it was far from perfect.
The average project looked like this:
These were the issues that led me to believe that there was a problem with our approach, and something had to be done:
Building the same components over and over for every new design system is tedious work, and resulted in a much lower amount of contributions as projects approach the end.
By not giving the system time to mature, we weren't getting the benefits that mature systems offer.
Why not use Bootstrap or similar? Open-source design systems didn't offer the cross-platform support and customization we needed, and they delivered products with a similar look and feel.
I started the project by digging deep into the problem and taking inventory of previous design systems to learn what went wrong. I also ran a comparative analysis with the top multi-product design systems, such as IBM Carbon and Adobe Spectrum. Finally, I interviewed the users: my fellow designers and engineers.
Here are some of the insights from the conversations with team members, and research done on previous design systems:
Designers and engineers want to make the system better over time through iteration. They don’t want to waste time contributing to a system they know will be abandoned.
Designers and engineers want to trust the system, but the low amount of contributions makes it difficult to rely on.
As a result of the previous problems, the team built more inconsistent experiences, which significantly increased the time spent doing QA design reviews.
Given that we were trying to build a design system to solve any problem, it would have been easy to fall into the trap of building a very complex problem from the start. Being the systems thinking nut that I am, I knew this approach would not work. So I decided to heed Gall's Law's advice, which states that "to build a complex system that works, you must build a simpler system first, and then evolve it over time."
So I started with the bare essentials: the principles to drive the system, a foundational set of styles, a minimal component library, and the procedures to update and keep the system alive.
By following this top-down approach, I was avoiding past mistakes, where we focused on the "What of the system" and instead allowing the Design Principles to drive growth of the system.
The one thing all top-tier systems have in common is that principles drive them. It is the principles that define the purpose of the system.
Principles are like a guiding compass if you will. They empower individuals to contribute, ensuring that updates align with the system's purpose. But they must be actionable and measurable to be effective.
After some iteration, I landed on a set of principles that were adequate to benefit the system and any product built with it.
Are we being as clear as possible?
Visuals, messages, and states should be self-explanatory. Eliminate ambiguity. Create familiarity and strengthen intuition, always prefer recognition over recall. Enable people to see, understand, and act with confidence.
Is this as simple as it could be?
What’s easy to understand is easy to use. Simplicity makes the complex understandable, so it is our priority to make everything simple. In the words of Einstein: “Everything should be as simple as possible, but not simpler”.
Is this welcoming and accessible?
Be inclusive to everyone. Our designs should also be mindful of the broad range of human abilities and disabilities, as well as the variety of devices, browsers, and networks people use.
Does this work in different contexts?
Systems must be flexible, able to accommodate different contents, and adaptable to changing conditions. The components should be modular, designed to work seamlessly with each other, in whichever combination serves the user's needs best.
Does this serve a meaningful purpose?
Everything in the system must have a purpose, and everything should be conceived with that end purpose in mind. The UI should be designed to serve the user goals, and account for how users will interact with it.
After the principles, the second cornerstone of the system are the practices. Practices are the procedures we put in place to ensure that the system stays alive and relevant. I came up with four core practices to fulfill this very purpose:
By documenting everything we removed any uncertainty and mitigated any of the trust issues contributors brought up during the interviews.
Therefore, we must make the documentation appealing by ensuring it is highly visual, easy to digest, and full of interactive examples. No one wants to read a wall of text for every component.
For the system to be useful, we must ensure contributors can find the information they are looking for.
We accomplished this by keeping the system as lean as possible, linking everything to their sources, and organizing the content intuitively (informed by card sorting exercises with designers and engineers).
Feedback is often underrated, but as the system evolves, we must constantly assess what is working and what isn't. To do this, I established biweekly meetings to reflect on the current status and decide the next steps.
One of the things we learned during these status meetings was that contributors didn't know when to create components, so I documented a decision diagram to help them troubleshoot on their own.
Building and documenting the system is not enough. For the system to be effective, the team has to use it. And to get people to use it, you must get it in front of them. Adoption is key!
It took a lot of preaching to get it done, from one-on-one meetings, design reviews, and video tutorials, to adding a changelog to post the latest updates. It was not a once-done-and-forget job but a continuous ongoing process.
Foundation, also known as design styles or tokens, refers to the lowest level design decision that define the look and feel of the UI. For StartupJS, our foundation refers to: Typography, Color, Spacing, Radius, Motion, Elevation, Iconography, and Imagery.
To keep the system lean, I chose to limit the number of presets for all foundational styles. For example: for typography, I used 7 presets + their bold BEM variants. While for colors, 9 styles with 4–5 steps were more than enough. Despite how limited this may seem, I never felt the need to add additional variables.
By adhering to a typographic scale and a color-generating algorithm, I ensured that any designer in the team could generate typography and colors that work by default.
We built StartupJS to be flexible enough to accommodate any brand style.
How do we achieve this? At the start of a project, we work with clients to establish the visual direction of their product. To expedite this process, I built several style-sample templates I can use to show clients a glimpse of the final look of their app early in the process. This way, there are no surprises, and we can focus on solving the real challenges.
This is one of the style templates showcasing the StartupJS default styles and other example business cases.
Picking these values is is a lot like picking ingredients to cook a meal. When you rush the process and pick ingredients that don’t work well together, you end up with something nasty. But if you take the time to pick the best ingredients you’ll get delicious results.
I followed Brad Frost's Atomic design principles to build our component library, starting on the simplest components first, and building up to the most complex patterns.
In the design system, these are the simplest UI components we can build using a few elements, these include buttons, labels, links, badges, tooltips, etc...
Complex components are assembled using simple components. Some examples are cards, time/calendar pickers, drop-downs, form inputs, etc.
Sticking to a single design system allowed us to improve components and offer component variations to clients. See how table filters evolved to meet the demands of the different product use cases.
Mobile tables also went through extensive iteration, as displayed below.
As I was working on pattern components, I realized that some user flows rarely change, or just require minor tweaks. By pre-building these user patterns upfront, we would be able to deliver a working experience for new projects out of the box. These Patterns include things such as account creation, recovery, login, cart checkout etc...
As the system matured, we found that it would be beneficial in the long term to add CMS-like features, such as admin panels, as an out-of-the-box features included with any new project.
Even though end users rarely experience these behind-the-scenes panels, admin panels are great time savers for designers and engineers, who can now test features faster. Additionally, they allow clients to manage their digital product transactions, mailing, and user base.
Since we started building StartupJS, we’ve built 19 cross-platform products. These products serve brands in a wide range of sectors such as: hospitality, education, finance, logistics, technology, human resources, SaaS B2B, and retail.
StartupJS has proven to fix most user problems and is only improving as it matures. Additionally, it racked incredible results across all the success metrics I selected.
Each designer saved an avg. of 30h every month over the first year. This number almost doubled after the first.
Keeping the system up to date and educating users resulted in more consistent designs delivered, which reduced QA time.
The efficiency gains allowed the team to almost double the sprint work, when compared to our previous approach.
We reduced the time it takes the team to launch the first version of any product from 7 to 2 weeks.