Typography systems are collections of individual styles used across an application. Everyone knows that their system should include typography on some level, but the difficulty is defining that system in a way that is easy to understand by designers and developers, and enforces consistency while allowing for flexibility over time.
How do you define a core typographic style?
Typography is one of the most basic building blocks of any UI, but also one of the most misunderstood. Without taking careful measures, these systems can be broken from the instant they’re created. In this section we’ll look at how we approach the definition of a system that’s built for change
Understanding How Typography is Defined
Defining typography in a design system can be difficult because of the biases we bring along. In word processors, design programs, and even CSS, we’re used to defining typography holistically, including everything from weight to line-height to color to spacing.
Typography = Family, Size, Line Height, Color, Spacing, etc.
This holistic view of type styling leads to a system that is bogged down with unnecessary stylistic details.
To alleviate this over-definition, many frameworks swing the pendulum to the other extreme: an hyper-compartmentalized model of definition like Tachyons or other atomic CSS models. This has its own downfall: it doesn’t produce consistent usage and can leave system users with a paradox of choice.
Typography = [Family] +[Size] + [Line Height] + [Color] + [Spacing]
If you approach your design system typography using either extreme, it will break down. Handling type in a design system requires a new, nuanced, and correctly compartmentalized way of thinking about how type is styled and defined.
Don’t Conflate Style and Output
One of the earliest warnings we can give anyone building a design system is to never conflate style with output. It is the single fastest way to blow up a typography system.
The reality is this: typography styling is, and should be, applied independently of HTML output. Two of the same HTML elements can have totally different styling in different contexts, so those styles need to be abstracted from specific code. Trust us, your developers (and system) will thank you.
Coupling the Right Characteristics
Typography is complex, having multiple characteristics that make up an individual style. But which characteristics make the most sense to couple to a type style? Which ones create the most headaches? We answered that for Ether and our own systems by looking at inherent and contextual characteristics.
Inherent Characteristics are ones that are guided by the rules of typography itself. They don’t change based on context, and they include:
Contextual Characteristics can change based on the typography’s uses and goals in the interface. They include:
- Spacing (padding, margin)
Coupling contextual characteristics to typography will do only one thing: exponentially balloon the number of styles in your design system. To keep things manageable we make sure to always define color and spacing separately from type—a lesson we had to learn the hard way.
That increased level of complexity can make a system a bear to manage, and even more difficult to implement. By keeping our styles from defining spacing or color, we insulate them from taking on too much weight, and allow our components to do their job—pairing inherent and contextual characteristics based on the contexts they address.
In the end we’re left with a system that works like this…
Typography = [Style] + [Color] + [Spacing]
…with all of those aspects being defined and maintained separately. Each one can change and grow without impacting the others, and each one can stay focused and succinct, keeping our system from growing to the point of failure.
How many styles should you define?
The complexity and quantity of your typography styles depends largely on the context of your application, but there are some basic ways to work through your needs as you develop a system.
How Typography is Used
First, you should look at how typography is used across your product and system. For our systems, this has meant defining typography along 4 general purposes—each one with multiple styles:
- Body text
- Button text
- Input text (Labels, Fields)
Once you understand how your styles are used, you can find overlaps that allow you to minimize the overall number of styles necessary to build your interface effectively. Then you can move on to another typical typographic rat’s nest: sizing.
How Typography is Sized
Proper use of typography requires using it consistently while establishing clear hierarchy. We’ve all seen codebases with 21px, 24px, 28px, and 32px type all separately defined. This haphazard application of hierarchy should be something that a design system guards against, but it can easily become (and erode) the system’s foundation when definitions include similar but slightly-different sizes.
To create and enforce this hierarchy we use a sizing scale, defining a base size and a ratio that determines how exponentially sizes increase across styles. This works because as sizes get larger, slight variations become less noticeable—and less useful. Instead, we can eliminate unnecessarily similar sizes and ensure that there’s a broad range of usable sizes in the system.
You can actually take our simple generator for a test drive in this handy Codepen example. Instead of using a static size format like pixels or points it uses the little-used ex unit to define size based on the x-height of the typeface itself.
Bringing Size and Purpose Together
Using both of these concepts we can then construct a system that looks like this, using Ether as an example:
- Headings (4 sizes)
- Body text (3 sizes)
- Button text (uses Body)
- Input text (uses Body)
This method of defining size gets the style count down to 7 core sizes, more than enough to enforce consistency while allowing for flexibility in the design of the UI components.
How do you keep typography colors simple?
Decoupling color from style is a great place to start, but you still have to attach color somewhere along the way. In building out system components and UIs with larger teams we found that over time designers were using varying, but similar, shades of gray as the product grew, and sometimes text was being specced at colors that failed accessibility standards.
Contrast is Your Friend
In order to keep the amount of color choices simple for developers and designers alike, we turned to a foundational design concept: contrast.
We wanted to define contrast points that would provide differentiation without overloading the system with options. We also needed to make sure typography could be colored semantically for those specific purposes where a color’s meaning is important. That led us to define a typography-specific color abstraction layer like this:
- Dark (Full contrast on white)
- Medium (Mid-level contrast on white)
- Low (Low-level contrast on white)
- Light (Mid-level contrast on dark)
- White (High-level contrast on dark)
- Semantic (Success, Info, Warning, Danger)
Along with the benefit of maintaining consistency and accessibility, these color abstractions have an added benefit: they are change-proof. If the design team decides to change the hex value of green used for semantic success, we can rest assured that change will happen globally in the appropriate contexts only—no find-and-replace necessary.
Typography has the ability to make your system a joy to use or an eternal nightmare. Using a couple of these simple tips can go a long way towards helping your system stand the test of time and use.