Designing Systematic Colors
How to make themable, flexible, WCAG 2.1 compliant color ramps for a design system
Mineral UI is the open source design system that CA Technologies is building for use by all of our products. Color is a core building block of any design system, and it has proven to be one of the most challenging design problems we’ve tackled, due to the wide variety of requirements:
- Mineral has accessibility features built in by default, so all of our color usage needs to meet WCAG 2.1 Level AA recommendations for contrast.
- Mineral is themed for consistent styling, and color ramps can be swapped out at any time. Ramp values, not individual colors, are applied to an interface element. For instance, the color blue-60 has to be the same perceived contrast as red-60 or teal-60.
- Mineral will eventually support a dark mode, and we wanted the color ramps to be visually appealing in either a light mode or dark mode UI.
- Color is used to describe status. Many of CA Technologies products are used to describe and/or report on the status of different things, such as destructive actions, story progression, router availability etc. Color is an important tool to provide at-a-glance meaning, so we maintain several UI component variants as foundational concepts in our system. These colored UI variants need to work harmoniously with all of our color themes.
While attempting to solve this problem for ourselves, we stumbled upon a methodology of creating a themable, flexible color palette.
Mineral UI is an open-source React based design system created to build appealing modern software experiences. It is the culmination of designers and developers working together to give teams the ability to ship high-quality products faster.
Scoping from Hindsight
Creating a color palette is one of the earliest things we thought about when creating the design system, since it’s so fundamental to design and development decisions. Similar to many of the things we’re learning, we see seemingly obvious issues in hindsight, and our desire to improve has pushed us to explore the fundamentals of others’ decisions before making our own.
Change management is hard, because color is so far-reaching. We’re finding that making seemingly innocuous changes to color is very disruptive to teams who have already started to build products based on Mineral, so this most recent color revision represents our goal to prevent breaking changes going forward.
When scoping color for our revision, we determined our palette needed to contain the following attributes:
- Theming: Themes provide a consistent look and feel across products, and eliminates guesswork.
- Thematic harmony: Each color theme should have the same value steps in tone.
- Distinct hue breakpoints: Many of our products are sold in modules or suites, so the colors need to be similar enough to feel like a family, yet distinct enough to indicate to a user that they are in a different module within the product family.
- Scaleable and additive: Color palette should have a systematic pattern which can be added to as the needs of the design system grows.
- No breaking changes: Going forward, we wanted our color system to work in any situation so there are no more disruptive changes to designs that currently use Mineral.
- Accessible: Our goal is to make sure everyone has a great experience when they use our products. Mineral supports Level AA color contrast. Visual presentation of text must have a 4.5:1 color contrast ratio in every UI component.
We did a deep-dive analysis of other themed color palettes that use ramps, by investigating palettes from IBM Design, Material Design, and Open-Color. (Sidenote: If you’re interested in exploring other design systems, UXPin’s Adele is a great place to start.) The purpose of the research was not to copy and paste their ideas, but rather to “look under the hood” and understand the underlying decisions behind their color selections.
During the research, we focused on the following:
- Hue: Does each color palette use a single hue value? Or do hue shifts occur across one ramp?
- Pattern: Does value selection show a steady pattern? If there is a pattern, is there a consistent technique used across the other palettes?
- Saturation and Brightness (lightness): How Saturation and Brightness work when each value increases or decreases across the ramps?
RGB, HSB, HSL, or HEX?
Color modeling is a complex topic that deserves its own article. But briefly, here’s what matters: In these examples I’m working with HSB (Hue, Saturation, Brightness), because it is one of the more user-friendly color pickers — perfect for inspecting and tweaking colors. Yet, HSB (360×100×100= 3,600,000 colors) can only express about 21.5% of the RGB color space (256×256×256=16,777,216 colors).
HSB is a good tool for exploring color, but to avoid rendering issues between different interpreters (browsers, design software, displays etc.) the final color definition is most accurately recorded as RGB or HEX values. Another important note: HSL and HSB are separate models, which have different principles, and should not be confused. These examples are in HSB.
I took example color ramps from each design system and mapped each swatch to a Saturation and Brightness grid. I also tracked hue shifts within a ramp.
IBM’s Color palette uses the full range of saturation and brightness, but it was challenging to identify uniform patterns among the different ramps. When looking at Green (see below), IBM broadly shifts the Hue value between 78 and 120, which can be difficult to consistently replicate and manage within other colors. IBM clearly values accessibility, because the contrast ratio is consistently designed to be 4.5:1 or higher when the value is 50 or higher.
Material Design values quickly change in direction after value 500. Some colors may have various accessibility issues because some of the base colors are brighter than other colors across the palette. Of course, the problem can be solved by flipping the text color when switching themes (using black instead of white text, for instance) but we want a solution that can handle text color consistently across themes.
Open color is built on elegant curves. Look at the placement of color on the HSB grid, and an intentionally curved color pattern emerges. The color selection is on-point, and reflects current web trends. The contrast ratio is somewhat lower, so it didn’t quite fit our desire for the company’s products to be accessible by default.
- IBM has made it possible to use color without worrying about accessibility issues, because each ramp can be consistently applied to various products; they did a good job of consistently distinguishing color contrast ratios for each value (more on this later).
- Material design helps create products through a wide variety of color schemes, but taken as a whole, the palette has some inconsistency.
- Open Color has the most identifiable pattern, and the palette was designed to be easily scalable when adding other colors.
- Interestingly, most color palettes use a hue shift to define color values across the ramp — especially IBM.
Designing the Color Palette
The Mineral theme system contains a gray ramp that is used across themes, and several theme ramps. When we refer to “color palette,” we’re discussing the theme ramps.
The first thing I did was to create a set of rules based on the project’s requirements. These rules are intended to be scalable, so there is clear guidance for extending the color palettes with more options in the future.
How to Define Theme Swatches in Three Easy Steps
- Base color swatch: Base color is the dominant color within the themes; it is the swatch that will be used the most throughout the UI components. Theme-60 is always the base color for light templates (white text on theme color) and theme-50 is the base color for dark theme templates (black text on theme color). The contrast ratio of these two colors is always 4.5:1 or higher.
- Swatches near the base color: These colors will support hover, active, and focus states for the base color in actionable elements such as buttons, links, and form elements.
- Beginning and End swatches: These swatches are most often used as accent backgrounds colors, or to give visual weight to a UI element, in both light and dark themes. For instance, theme-10 (light theme) or theme-100 (dark theme) could be used as backgrounds on selected items in a drop down menu or table row.
The Version 1 Approach
Before diving into the current color revisions, it’s useful to understand how we arrived at version 1 as it was the basis for our efforts.
First, we started by choosing a base color. This decision can be quite arbitrary, though we were shooting for high saturation colors that had a contrast ratio of 4.5:1 or higher. At this point, we were also assuming we wouldn’t have a dark theme, and that we would programmatically swap the text color between black or white, depending on the contrast ratio.
At the same time, we chose each end of the ramp — light (theme-10) and dark (theme-100) valued colors. The light colors have a brightness in the 90s, and the dark colors have a brightness in the 20s. The theme-10 swatch colors are significantly less saturated than the theme-100 colors.
After defining these key values (10, 60, 100), the main colors by each theme became apparent.
Sidenote: Compare the version 1 colors to the colors in version 2:
The first noticeable change is the missing colors. That’s right: Lime, Yellow and Orange colors are gone — and the Bronze color was born. Here’s why:
- Lime was deleted because the team decided to remove the code which programmatically changed text colors based on contrast (to reduce complexity). If we edited the Lime color to be 4.5:1 contrast with white text, it no longer looked like lime anymore (more like goose poop).
- Yellow is equally difficult to make into a contrast-compliant color theme. If the color contrast ratio of yellow is more than 4.5:1, it would look like the current Bronze color.
- We got feedback that Orange, used for the “Warning” variant in our theme, was easily confused with our “Danger” variant (Red). Bronze was born as a compliant way to provide some visual distance from “Warning” and “Danger” variants.
Back to the Version 1 method:
Now, it’s time to draw a color curve and distribute the remaining swatches on the ramp. The goal was to make a smooth, curved distribution. Each curve pattern will be slightly different depending on the location of the key colors (10, 60, 100).
Finally, we list each color value. The gaps between mid values are much wider than the other values, because brightness needs to distribute more evenly along the curve.
Here’s some example curves from the Version 1 palette:
And here is the final v1 palette, in which all of the colors were created from the same rule.
The v1 color palette initially met most of our requirements — or so we thought. A few issues emerged as we started to use the color palette in actual product design work:
- Teal, Slate, and Dusk, which are relatively low-saturated themes, are visually distinguished from other high-saturation themes. They looked odd when placed against the gray ramp that underpins the system.
- When converting the color palette to grayscale, it does not have an equal increment for each value across themes, so a value-to-value theme swap (blue10 to green-10 for instance) created unreliable visual / contrast results in some instances.
In the picture above, besides base colors, most color values do not have the same gray values between themes. Therefore, it’s difficult to swap out themes reliably while maintaining uniform contrast.
Once we discovered these issues, we came up with the basis for a new hypothesis: Can we create a theme system that maintains uniform contrast jumps between adjoining (theme-10 to theme-20, etc.) swatches in each ramp?
Let’s dig up previous research materials from other design systems and see if they solved this problem:
Material Design and Open Color do not have the same gray tone for each swatch level on the ramp, across themes. But IBM is closer with a seemingly consistent gray pattern (It’s not perfectly the same value — Green seems relatively bright).
From this review, the team discussed the value of consistency:
- What are the benefits of having the consistent gray values?
- Is it possible to create a system with exactly the same gray value, for a given swatch, across all themes?
The first question is difficult to answer unless tested in real UI scenarios. The second question is a bit easier to answer.
The Version 2: Updating the Color Palette
When we set out to fix the contrast consistency issue, we also decided to address a request from nearly all of our business units: a dark theme, which is commonly used on HUDs (Heads Up Displays) in Operations Centers and other monitoring environments. The v1 color palette used super-saturated colors at the darker end of the ramp. When we placed these colors on a black background, they looked out of place.
For version 2 of the palette, the work we did earlier was augmented with a few additional steps and a new color curve that made a dark theme possible:
1. Create Gray Pattern
As shown in this chart, the “easy” solution might be to increase values using uniform spacing between each value. I set theme-10 at 95% brightness and theme-100 at 15% brightness. For each step, we decreased brightness by 9% as theme values got higher.
After testing some key colors in our system, we realized that this approach is a good start, but still not ideal.
Looking at theme-60, it’s brightness value is 50%, which is not Level AA compliant (ratio is 3.95:1). This result led us to try a more refined “mountain-top” pattern:
We set theme-10 to 95% brightness, theme-60 at 45% brightness, and theme-100 at 15% brightness. We then tried different patterns of inconsistent increments that followed a “rise, peak and fall pattern”, and found that if the values rose equally, lower or higher values appeared to be significantly different than those in the middle.
2. Creating the color pattern
Now that we had a gray ramp that worked across themes, we turned our attention to hue and saturation.
The approach we took is similar to v1, with one big difference: we used an oval curve to reduce the saturation of the darker values, so they would look natural in a dark themed UI.
While creating each curve, we tested some existing UI components for both a light theme and dark themed environment. This helped us quickly determine whether the curves produced a consistent appearance across the different theme ramps.
Most of the color themes use a circular pattern, but Red, Magenta, and Bronze use an oval pattern because, due to the brightness value of the chroma, circular patterns peg out saturation at 100%, which looks terrible on a dark UI.
To create this “perfect pattern”, continuous verification of consistency in the grayscale ramp during the creation process was necessary. We chose to copy and paste each swatch value in Photoshop to see that it exactly matches the grayscale values set earlier.
We made this work easier by writing down each color value in one place, a practice that is especially useful when the work needs to be referenced by others later. At this point we also decided it was a good idea to record an accurate RGB or HEX value for development. We chose to use HEX.
Repeating this process for each theme color yields an accessible, flexible theming system as shown below.
Converted to grayscale
Now all themes are made up of a unified gray pattern. (Our theme-gray has intentionally set values that differ from the theme color values, so it doesn’t display the same grayscale pattern.)
- This is just the beginning of dealing with the complexities of color, accessibility and consistency in Mineral UI. By spending the time now to create a scalable color system, we believe it will be easier to address unknown issues as they present themselves. In hindsight, it was definitely worth spending the time early in the process to invest in this effort.
- The process outlined in this write up from beginning to end was about six months. During this time, teams started to use Mineral and challenged some of our early assumptions about addressing accessibility and dark themes. We’re finding that culture shapes design systems as much as the products themselves.
- What we did not include in this post is design validation. A lot of time was spent with the team discussing how to apply the themes and testing them in real world product mockups.
Special thanks to:
- CA Central Design Team: Albert Tan, Aaron Sagray, Sun Young Han, Matthew Talebi
- Mineral UI Component Team: Mike Waite, Mike Holm, Kyle Gach, Brent Ertz, Victoria Vasys.
Quick and Easy Solutions for Color
If you don’t have a complex color problem, try using some of the various color palette generators. Here are some we found very useful:
Unfortunately, there seems to be no satisfactory color palette generator yet capable of fine-tuning and creating themable ramps. If you happen to know of any good tools, please leave a comment.