Building NomadHair: A Journey in Component Driven Development Part I — Laying the Groundwork

Luke Nam
7 min readJun 16, 2024

--

Illustration by Diana Lightfoot

Table of Contents

Background

To implement UIs in modern days, developers need to consider countless UI variations between accessibility guidelines, viewport sizes, browser types, async states, and evolving business requirements. As the application size grows, the complexities also increase exponentially.

Without proper systems in place, UIs can easily lose coherence and become harder to debug.

I had to learn this the hard way in my previous job as a frontend developer. At that time, my team was revamping UIs for our flagship product, and I realized that there were several issues with our approach:

  1. Long feedback loop to verify the UI components: Product managers and peer developers had to wait until an entire feature was built before they could verify and reuse UI components for product development.
  2. Rigid, monolithic components: Because we were developing UIs mostly at the page level, many of the components we built were tightly coupled with business logic, making it difficult to change or update without affecting the whole page.
  3. Absence of a UI testing system: Without proper UI testing in place, maintaining and updating UI became more challenging, making the app brittle and bug-prone over time.
  4. Lack of design resources and documentation: Having no clear visual references on UI often made it difficult to reason about the business requirements, not only between developers but also with stakeholders.

As a result, UIs became unwieldy as the application grew, and it became increasingly complex to debug and time-consuming to ship.

Facing these problems was definitely a challenge, but at the same time, I began to ask myself:

“How do you effectively build UIs in a way that is maintainable, fast, and scalable?”

One of the answers to this question that I found was Component Driven Development (CDD).

CDD is a development methodology for building UI with modular components. With this approach, UIs are built “bottom up” starting with the atomic components then progressively assembled into pages.

The main benefit of CDD is that it improves the modularity of your components, which in turn allows you to build more UIs at a much faster pace simply by reassembling components.

I’ve learned that companies with strong brands and excellent UI/UX, such as Airbnb, Uber, and Shopify, have already been using some form of CDD in their development process.

I won’t be going over too much about what CDD is and its benefits in this post, as there are already many great articles that deal with these topics.

If you are interested to learn more about it, check out the article from Eden Ella.

To put this programming paradigm to the test, I have been working on a project — NomadHair.

NomadHair is a full stack appointment scheduling app built to help my friends easily arrange haircut appointments with me. 💇‍♂️

In this series of articles, I would like to share how I have learned to solve problems I faced by building a project in component driven approach.

🏗️ The first part of the series deals with laying the groundwork for the project. I will go over my thought process and reasoning behind my design process and how I established a design system for NomadHair.

🧩 In the second part of the series, I will be covering more technical aspects of how I built UI components in isolation using Storybook, as well as React component design patterns to improve reusability.

🧪 The last section covers types of UI Tests I implemented in this project and how I have automated these tests with Github CI/CD to create a more productive workflow.

For your reference, here is the link to live site and as well as the Figma mockup.

Defining a system

Much of the work during the early phase of the project was put into establishing the design system — a single source of truth for styles and common UI patterns.

In order to go component-driven, I needed to first think about which components are going to be reused and which set of design elements should be systematized (color, fonts, spacing, etc.).

i. Style Guide

I started by creating simple UI layouts using Figma based on the minimum-viable features:

  1. Landing page
  2. Appointment Forms
  3. Appointment management page for user.

While creating these layouts, I looked for repeated use of color, font sizes, or other styles. If any elements were to be used throughout the project, I systemized them. This way, I could avoid making the same design decision twice.

For the scope of this project, I chose to start small. Instead of attempting to establish every single design rule, I focused on key elements like color scheme, typography, and logo.

Brand logo, colors, and typography
Brand logo, color scheme, and typography

Using Figma in this process was extremely helpful, as I was able to store these systems as design tokens. This allowed me to make design decisions more quickly as I created new UIs, without having to hand-pick values on the fly.

ii. Accessibility

One thing I paid more attention to was ensuring that the color contrast met the WCAG accessibility standards. I believe that colors are one of key ingredients that determines user’s first impression of the website, and it was important for me to get this right in the first place. Using Figma plugin, Contrast & Accessibility Checker, I made sure that text has a contrast ratio of at least 4.5:1 against the background color.

iii. Identifying UI patterns with Atomic Design thinking

Once I had good amount of layouts designed, I then looked for the common UI patterns and broken them down into a set of reusable pieces.

To do this, I referred to Brad Frost’s Atomic Design principles and grouped them in 3 different categories:

  • Atoms (Avatar, Button, Input, Label, Card)
  • Molecules (Card, Calendar, Modal, Form Inputs)
  • Organisms (Header, Footer, Section, Forms)

iv. Designing component variants

In a component-driven approach, the design of a component should reflect its relevant states, such as size, style, icon position, label, etc. Figma provides a feature called variants to assist with this process.

Take a look at the button component below.

The components inside the purple dotted line represent each variations of the button. Once I have all these defined, I can simply use the button component anywhere in my Figma file and simply configure it with a few clicks.

Thinking about component variants from the design stage was extremely helpful, because this allowed me start considering the APIs for the UI components later in the development phase.

In Part II of this blog series, I’ll provide more detailed information on how these variants are translated into reusable code using Storybook.

After several iterations of the design cycle, I was able to finalize the UI layouts required for the project, including mobile layouts, of course.

Checkout the final design here.

Why bother with all these?

Nearly a third of the entire project was spent on this phase. Progress was slow in this stage since it required a lot of thought and attention to detail to establish systems for decision-making. Even deciding on a primary color required deeper consideration of aspects such as color theory, accessibility, and design tokens.

So, you might be wondering, why bother with all this work?

There are a few reasons for doing this:

  1. Faster design iterations by avoiding making the same design decision twice.
    The more systems you have in place, the faster you’ll be able to work, and the less you’ll second guess your own decisions. Because you don’t have to handpick values from a limitless pool, you can avoid decision fatigue and instead focus more on the design.
  2. Maintain the app’s cohesive and uniform appearance, even as the design evolves over time.
    With a design system, you are deliberately limiting your design options. This may seem counterintuitive to any creative effort, but in the long run, it is the key to building more visually coherent and beautiful UIs.
  3. Keep the design process and development in sync with a single source of truth.
    Many companies that adopt a component-driven approach often maintain design systems parallel to their products. This is because in CDD, both designers and developers need to be able to think of UIs in terms of components. And design systems bridge the divide between the designers and developers, facilitating a more productive workflow and collaboration between both groups.

It’s a bit more work upfront, but I believe it was worth it. As the project progressed, it became much easier to update the UIs or create new layouts simply by composing different sets of components together.

In the next post, I’m going to cover the technical aspect of implementing these UI designs using Storybook and React.

--

--

Luke Nam

Taking one day at a time - cultivating my digital garden :) 🌳