How to define color usage through semantic sets for design systems

You’ve created all your color ramps, but how the heck do we start defining how they will be used? Let’s talk about it.

Katie Cooper
UX Collective

--

Before we dive in, make sure you get caught up on how to pick out your brand colors and how to create a color ramp.

🎨 Follow along in Figma: Community File

What is a primitive color?

A primitive color is an individual color from our catalog of brand colors, purple.100, purple.200, etc. It is the most atomic form of color and will be attached to a semantic color name.

Color ramps make up a set of primitive colors for green, orange, purple, and teal.
A primitive color set. Dot = brand color.

What is a semantic color name?

A semantic name for a color defines how a primitive color will be used throughout a design system. e.g., foreground.primary.

Primitive colors have more pragmatic names, such as green.200, purple.500, etc. for selected HSL/Hex values because no one will remember #7748F9. These primitive names don’t provide any information about how it will be used within the system. Is it a background color for our primary button? A border color for our focus state? No one knows, and this is why we rely on semantic naming.

Semantic naming is essential not just for color but all other foundational styles. I’ll continue to touch on this concept throughout this design system series.

Flow diagram of 2 primitive colors being attached to their own semantic colors and resulting in how they are applied to a primary button.
Example of two semantic color definitions for a primary button.

In the example above, the primitive color purple.500 is attached to the semantic name background.interactive.primary.default. This clearly tells me that purple.500 will be the background color for the primary button in its default state.

The primitive and semantic relationship

A semantic name doesn’t replace a primitive name. Primitives attach to a semantic name because you can detach and change the primitive color associated with a specific semantic name.

A GIF showing a flow diagram of a primitive color → semantic color → resulting background color of a primary button. The primitive color value is initially purple.500 and it gets changed to green.600 which changes the value of the semantic color and button background color to green.600.
Changing the primitive color value updates the semantic color value and any components with that color applied.

In the example above, you can see that we are updating the purple.500 to green.600—which automatically updates the semantic color. Once you establish your semantic names, they shouldn’t get changed often, if at all. You can start to see how this can play into theming. To create a dark mode, you only need to change out your primitive attachments.

If you’re familiar with design systems, you’re probably thinking, “But you missed a semantic level that defines your brand color!” This is intentional. It’s not required for a successful design system. However, I will cover strategies for this in my next article, focusing on advanced tactics for color theming.

Creating semantic color sets and naming conventions

There are a variety of strategies for semantic color naming; I will share what has worked for me when building multi-brand systems. Collaborate with your engineers because they will use these names in their code; you have been warned!

What’s with all the periods and lowercase names?

We want our design files to align with code as much as possible, so how we name our styles is important. Engineers typically prefer lowercase naming, but you may also see camelCase, kebab-case, or snake_case for multi-word needs.

The periods between names are equivalent to the backslashes / used to create folder structures for styles in Figma. If you’re familiar with design tokens or using Tokens Studio, this is also how the .JSON file engineers use will be structured. You could think of it like a breadcrumb.

An example of Figma colors styles UI and an example of style code in a .JSON file.
Left is Figma color styles. Right is .JSON code via Tokens Studio.

In the example above and to the left is an example of what your styles may look like. “foreground.primary” or “foreground/primary” creates a primary style within a foreground folder. This same concept would apply to Figma variables.

Top-level semantic color sets

Semantic color sets can be broken up into 3 main categories that all work together to style all UI elements.

  • Foregrounds
    Text, icons, and any elements that sit on top of a background.
  • Backgrounds
    The background color of individual UI elements and whole sections or bodies of content.
  • Borders
    The stroke or outline color of individual UI elements or lack thereof.
Example of semantic color documentation for foregrounds, backgrounds, and borders.
Example of semantic color documentation for foregrounds, backgrounds, and borders. Figma community file →

Styling a component

Before we dive deeper into sub-categories, let’s look at how foregrounds, backgrounds, and borders work together to style a component.

Example showing a success variant of a feedback banner component and color documentation for foreground, background, and border color styles.
Example of how foreground, background, and border styles work together.

The above example, we have a success variant of a feedback banner. Each category has a “.system.success” sub-category. We are still using foreground.primary and foreground.secondary for the text copy and tertiary icon button. However, using the foreground.system.success for the text copy wouldn’t be incorrect. The tertiary icon button would be left alone due to being a nested, interactive component with its own styling.

Sub-categories

You might wonder why “foreground.system.success” and not “foreground.feedback_banner.success” Flexibility, consistency, and the ability to scale. Multiple types of feedback components will utilize these exact styles. e.g., Inline alert or completed progress ring. A designer may also need to create a custom local component to communicate success to a user. If we did component-specific naming, it would confuse them about the styles they should use.

Let’s break down these categories to help you define styles for your system. Remember, every system has differing base styles, so how these shake out in the end will vary, but the concept should still apply.

Example of design token naming structures for semantic color styles.
Semantic naming structure examples.

System-wide color styles
There are color styles that get used everywhere across multiple components. If it’s not otherwise specified, it will use a system-wide color style. Some interactive components, such as a secondary button, will use “foreground.primary” and not “foreground.interactive.secondary.default” foreground style because we don’t need it. We might be reflecting states only through background colors, but if your system wants to show states through an additional foreground color change for the secondary button, then you would add those specific styles.

System wide styles for foregrounds, backgrounds, and borders.
Example of system-wide color styles.

Specific color styles—Intent or use
If a style isn’t utilized across the entire system, the second level will define intent or use. This second level typically includes interactive, input controls, system, and accent.

  • Interactive
    Most commonly, these are your buttons. Why don’t we just say buttons? Because we may have other custom interactions down the road that will use this same styling. These are further broken down into hierarchies—primary, secondary, tertiary, and quaternary. Each has their own states—default, hover, focus, press, active, and inverted. They will also have a disabled state, but all disabled components are styled the same so those use system-wide color styles for disabled.
  • Input Controls / Inputs
    Both input controls and inputs get styled the same; if they are not, you can break these up. Inputs include text fields, select, date picker, search, auto-suggest, text area, obfuscated, etc. Input controls include radios, checkboxes, super radios, toggles, input steppers, sliders, etc. These will have unselected and selected states with their own default, hover, focus, and press states. System styles would be utilized for their error and disabled states.
  • System
    System styles are used for all system components, and severity states for interactive components. This would include feedback banners, inline alerts, errors on inputs, etc. Their states are informative, success, warning, and error. Typically, the error will have a default, hover, focus, and press states because action is always required by the user in these situations.
  • Accent
    This bucket is broad and made up of secondary colors used within the brand. It’s reserved for what I like to call “brand moments.” This gives designers creative flexibility for onboarding, value props, and landing page screens as they have a bit more personality them. These are also used to create a variety of color combinations for informative badges.

These four categories, plus our system-wide styles, cover all essential components needed for a design system.

Application

If you’re creating a design system from scratch I recommend doing exploratory work before you begin defining your semantic color styles. Create a few key views that include multiple core components to get a sense for how these colors will look, then start to define your semantic colors. After so many times, you’ll be able to visualize it in your head!

🎨 It’s hard to show an entire semantic color system in a single article so be sure to snag a copy of the Figma Community file to see an entire example of semantic color sets.

Bonus tip for illustrations!

You can also use your primitive colors to create a semantic color set for your illustration library. This won’t get used by engineering, but it’s extremely helpful when you have multiple designers working on illustrations that need to be consistent. You can define your highlights, base colors, shadows, and outlines in multiple color ways. If you use a lot of people in your illustrations, consider making skin tone color ramps.

An illustration of a simple mountain range with documented semantic color styles is used.
Semantic colors being used for an illustration.

In conclusion,

Remember, every design system has different needs, so do what works for you and your team.

  • Primitive colors get attached to semantic names.
  • Collaborate with your engineers on naming structures.
  • All color styles will be divided into foregrounds, backgrounds, and borders.
  • There will be system-wide and more specific color styles for interactive, input controls, system, and accent components.
  • Don’t forget to document all the various states for the more specific color styles.

Next, I’ll dive into advanced color-theming tactics for multi-brand design systems where you can edit a single color value and have your primary interactive colors update along with their states through color mixing using the Tokens Studio plug-in. The concepts would still apply to Figma variables but color mixing is not available.

I love helping others on their design journey and want to keep the educational content I create free and accessible. If you feel inclined, you can support my journey by donating here!

By day, I am a Sr. Product Designer at Forge Studio. Where to find me:
Twitter, LinkedIn, Posts, Instagram, Website

--

--