Creating a Typography System With React and styled-components
Earlier this year, we were presented with a new challenge — build a new e-commerce experience from scratch. While this is, of course, a difficult thing to do, it’s also an incredibly exciting prospect. We get to make all the technical decisions from technologies and frameworks used to build the site to how we’re going to host it to what kinds of monitoring would work the best for our needs. What might not immediately come to the forefront of one’s mind, however, is typography.
When working on an existing website, one normally doesn’t put too much thought in to how the typography might work, where the styles come from, or where the fonts are stored. However, when building something from scratch, all these questions need to be answered. When we got to this point, we had a functional site with 12pt Times New Roman for all the copy. In order to implement the designs and have a site that will make people go 😍 instead of 🤮, we needed a solution for the type that would accomplish three things:
- Make it easy for us as developers to use
- Enable consistent type across the site
- Provide a maintainable formula for future developers when they do end up having to reason about typography in an existing system
First Pass — Typography.js
We built Harry’s new e-commerce experience with Gatsby and figured we should do our due diligence and check out Typography.js, which was made by the creator of Gatsby and included in most Gatsby tutorials and starters. Typography.js is slated to provide “a powerful toolkit for building websites with beautiful design.” While it did supply a lot of the functionality we needed, we ultimately decided to go with something that would offer us more flexibility and less overhead, along with the benefit of reducing the application’s external dependencies.
Since we’re using styled-components for all of the other styling on the site, why not use it for typography as well? The initial thought was to create a component for each style of text we’d want to use, and import those components as needed. Simple, right? Not quite: because we wanted control over which HTML elements got rendered for semantic and accessibility reasons, we had to come up with a way to apply the styles we wanted to any HTML element, while still fitting into the React and styled-components frameworks. Thus, we came up with the idea of
As you can see, this approach allows us to specify any tag we want as long as it’s present in the
DynamicComponent, like so:
This allows us to create a component for each style of type required, wrap a
DynamicComponent in it, and render any HTML text element with any style we desire, while preserving corresponding
children. Additionally, we export just the CSS styles so we can apply them in other situations where styled-components doesn’t work, like our
Great! We’re done!
…not so fast
This approach worked great for a while, but it soon became clear that we had way too many components, and as each page had visual designs applied, the list only kept growing.
Clearly this isn’t easy to grok or maintainable in the long run. We had failed in two of the three objectives of this endeavor.
Another unforeseen problem we encountered dealt with the responsive nature of the site. We soon learned that our designs included differently sized type for different screen sizes. Before it was obvious that it was widespread in the designs, we put some breakpoints in the CSS of each component affected, using different typography styles depending on the screen size. Unfortunately, this removed the responsive logic from the typography components and made the code messy. We needed a way to easily create new font styles based on their usage. The idea here was to have a component that followed most of our philosophies from before, but also included a responsive element. Because each piece of text behaves differently on different screen sizes, we began naming them after the specific purpose the text serves. For example:
While this solves the responsive problem, it only adds to the bloat problem, as we now have to create a new component for each time we need new responsive behavior.
Font Size as Props
The next step in our journey towards a simple, concise, and maintainable typography system was to eliminate a large chunk of these components and to pass in the font size as a prop. A large portion of the typography components are identical in every way except font size, so this allowed us to pare down the number of components dramatically:
Additionally, we can pass font sizes for different screen sizes and still use the media query in the components to render text responsively.
Hopefully this will be the one solution to rule them all.
Creating a simple, easy-to-understand, and maintainable typography system ended up being more complex than we had anticipated. It was an iterative process that may change more in the future. Who knows, we might throw away everything we’ve done and go with a completely different approach. Through experimenting and implementing different solutions while remaining flexible and nimble, we were able to create something that worked even if it wasn’t optimal, and that allowed us to continually improve upon it while working on the rest of the site.
Interested in working with us? We’re hiring!