One of the goals we outlined for the transition to React and TypeScript was “a well-documented, component-based design system.” We’ve made strides towards this goal and have built a solid foundation to continue enhancing components. There were many pieces that we needed to figure out to start our transition from a visually scattered user interface to a more consolidated and consistent experience.

Where did this come from?

As we brought on more designers last summer, one of the challenges they kept running into was learning about our design elements. We didn’t have a style guide that explained our typography, color palettes, formatting, and other visual styles, so we repeatedly heard the same questions from our new hires. “What fonts are used?” “How do we decide which button colors to apply?” “How much padding are different elements supposed to have?”

Originally built with Bootstrap, the application had a consistent feel. However, problems started to arise once our designers broke away from Bootstrap, and creating custom styles for new pages. Our engineering team implemented these new designs, many times with one-off classes and inconsistent visual styles.

Not So Common Components

A few examples of the various container styles and fonts

Design and Engineering Collaboration

As we’ve discussed in previous blog posts, the front end technology at Handshake has been transitioning from Knockout and Coffeescript towards React, Redux and TypeScript. Around this same time our design team tripled in size, from a lone designer to 3 designers all needing to produce cohesive design work. After a few chats it became apparent that now was the time to get both teams onboard with a new standardized set of components.

As the engineering team focused on moving forward with React, the design team started compiling current styles that were used and selecting ones to move forward with. While we could have started from scratch and rebuilt our entire design system from the ground up, it made sense to maintain some of existing styles while switching over to React without fragmenting the design.

Learning by Doing

As we started building out our common components we started to notice how quickly we could implement new features. By having a shared and standardized language between the design and engineering teams we no longer had to worry about creating a new button or text style for a one-off mock from the design team. As a team, we decide on design conventions to implement, we discuss when to re-use a component we currently have implemented and when to implement new pieces - resulting in a more consistent visual style across pages and projects. As we continue to grow and develop our component library we will be able to easily tweak these visual styles across all areas.

After deciding to move forward with common components there were some obstacles we faced.

  • On the design team, it felt constraining and challenging to know when to use specific components and when to break the “constraints” and let the creativity flow freely.
  • While we pushed forward on the engineering team, we struggled with communicating about changes to the component library, and how they should be used.
  • Another problem we faced was full isolation and encapsulation of these components in the codebase. Styles unintentionally leaked. We wanted to be able to remove old styles to deter CSS bloat, and easily hold a mental model of the component in working memory.

Our Processes & Tools

Storybook & Sketch Symbols

As mentioned above, one of the challenges we faced as an engineering team was documenting and sharing new common components. The problem with written documentation is that it’s difficult to visualize the components. We came across Storybook which offers a great mix of both visual and written documentation. It’s also great for developing new common components and trying different scenarios. Visual edge cases can be easily tested and documented.


An example of Storybook documentation for our Card component

On the design team, we’ve being working on fleshing out a sketch template file that has symbols that correspond to each component. By combining some really nifty uses of symbol overrides like those mentioned in Hacking the Button in Sketch and A Better Way to Make Buttons in Sketch , we’ve been able to simplify our processes and match components in our new mocks almost 1-1 with the components built out by the engineering team.


An example of Sketch symbols for our Button component

File Structure: Pods & Common Directory

In the process of migrating to React and developing our component library, we were having issues isolating components - especially common components that should be building blocks and and completely decoupled from all other components. We landed on moving our file structure to a “pod” based structure. Each component is its own self-contained “pod” or directory. For example, our Avatar component is a directory in the common components directory. Within the Avatar component, we have index.tsx which is the actual React component, along with style.scss which contains all styles for the Avatar component (which we’ll describe more in depth below with CSS Modules). Lastly, we have a nested spec directory which contains all of our test cases for the component.


An example of the pod structure used in our common component directory

This has some other small wins. For example, when searching for a file whether it be a spec, the component itself or the stylesheets you can quickly search the avatar directory for all related files. The avatar is a unique case because it has a few very specific sub components that can be passed as children, AvatarIcon, AvatarText, and AvatarImage which are all nicely encapsulated within the avatar component. Also by naming our components index.tsx we can still have clean import paths like import { Avatar } from "common/avatar";

CSS Modules

At the very beginning of our React and Typescript transition, we were relying on the Asset Pipeline to compile and deliver our React code along with the rest of our assets. This posed several concerns.

  • It was difficult to quickly adjust React components and styles because they were deeply nested in two different directories.
  • We had a mix of Bootstrap classes and got bit by the occasional classname collision.
  • To fix this we began using BEM naming conventions to “namespace” our classes. While this was a solution, we missed the succinctness of Bootstrap-style classnames.

CSS Modules remedied these problems. As seen above, our component’s styles are now defined alongside the component definitions in style.scss. We were able to rapidly work on both React components and their related styles with absolutely zero concern for styles “leaking” elsewhere in the application. We can use simple, meaningful classnames, such as .button without the concern of accidentally applying a style to every button or having verbose classnames such as .student-profile__button--success.

Another unintentional upside is code deletion. Even with some advanced tooling, it can be difficult to determine if CSS is truly no longer used, but with CSS Modules you’re guaranteed it’s only affecting this individual component so you can comfortably remove styles (🔥).

Sprinkling in some Experimentation

One of the design team’s early concerns about standardizing these components and picking a “best” option was sacrificing experience for consistency. As a designer, it’s nice to be able to explore new ideas without being constrained by current assumptions made by previous design decisions.

This problem was solved with the following strategies.

  1. During weekly design critiques, if a design is still in exploratory stages we don’t nitpick about matching the current design language. Experimentation is encouraged early on and designs are later adapted to fit the current standards. By doing this, we make sure we have the best solution first. Many times will realize a limitation of the current design system, prompting a small update or new component entirely.
  2. A few hours a week are dedicated to Design Experiments. Each week a designer will take an idea they want to explore and run with it - no constraints whatsoever. This gets us thinking outside of the current roadmap and ideas, and allows exploration of potential updates to experiences or visual styling, ensuring we don’t grow stagnant.

Final Thoughts

All of these processes and tools have been crucial in facilitating our adoption of common components. In addition, the teamwork from the design team’s inception to the engineering team’s implementation has been the largest contributor to our success moving towards a common, efficient and consistent experience. We think it’s critical to have both design and engineering on board, and share visible progress across the teams.

We’ve accomplished a lot in just a few months, but we still have a long journey ahead of us. When you work on this with your team, don’t worry about getting it right immediately. By just getting started we’ve discovered a lot about what we want to get out of common components and have already noticed improvements across our design and engineering teams.

If you’re interested in being a part of a team that recognizes the importance of creating a strong visual language through a component-based design system we encourage you to apply for our Software Engineer, Product Designer, or many other positions open across the company. Apply if you’re excited about changing the job finding experience for college students across the country!