In my previous article “Creating a consistent color palette for your interface”, I discussed creating a consistent color palette. This palette includes all the colors to be used in your interface. Let’s call it the Core Palette.
However, a consistent and accurate Core Palette is not enough for a consistent and accurate interface. We need a layer of rules about how to use these colors in the interface: which specific color to use for primary and secondary text, for input borders, etc.
Let’s introduce the Semantic Layer. The Semantic Layer is a list of rules for how to use colors from the Core Palette. So..
The Core Layer shows all the available colors, while the Semantic Layer defines how to use them.
I’m sure that most of you have seen really complex semantic palettes. They specify color tokens for each design system component, such as button-background, button-text, button-icon, etc. I have also tried this approach before, but now I can say that it is excessive and too tricky.
Such a system is difficult to build and even harder to use. It can be a third layer under the simple semantic layer if you really need this level of detail. However, don’t proceed with this complexity unless you are certain that it is necessary.
The simple semantic layer I’m showing will work in most interfaces.
There are 5 main groups of the semantic tokens:
- Text — colors for titles, paragraphs, captions, links, labels, button titles and etc
- Fill — colors for button containers, icons, checkboxes, toggles and etc
- Stroke — colors for input borders, dividers, banners borders and etc
- Layer — colors for page backgounds, popup backgrounds, overlays and etc
- Effect — colors for shadows and touch feedbacks
At Aloha, we decided to skip Strokes and Effects for now. For Strokes, we chose to use colors from the Fills palette to simplify our system. Later, I’ll discuss the pros and cons of this decision. We don’t need special colors for Effects because we use standard effects from the Android and iOS libraries.
In each semantic palette, there are a few semantic tokens. For example: text-primary
, text-secondary
, text-brand-primary
, and text-on-accent
.
In Figma, we have two separate files for the Core Palette and the Semantic Palette. We use variables to manage color tokens. Tokens from the Semantic Palette refer to tokens from the Core Palette.
A very valuable benefit of using the Semantic Layer is interface theming. A well-organized Semantic Layer allows you to easily manage light and dark modes, as well as color themes. At Aloha, we have 5 color themes, each available in both light and dark modes.
For each semantic token there are associated colors in each color scheme and mode. At Aloha, we only redefine brand tokens, but if needed, the entire semantic palette can be redefined for each mode.
Figma variables make it simple to manage and use color modes. You can set up your palettes in different modes and switch themes for your interface with just one click.
A few tips for a good dark mode
- Don’t simple invert the colors light → dark or dark → light, think about each particular case.
- The dark theme should have more depth. Surfaces that are closer to you should be lighter.
- In contrast to the light theme, separators and borders in the dark theme should be lighter than the background.
Fill colors are used for the
- backgrounds of buttons, tags, banners, etc. 🚨 Fill colors are not used for page backgrounds, bottom sheets, or popup backgrounds.
- fill of icons
- fill of checkboxes, radio buttons, toggle switch and etc
Don’t forget about elements on dark or colorful backgrounds. It is a very, very important token. Adding just one fill-on-accent token is enough to cover most cases.
Obviously, Text colors are used for text. We use them for titles, paragraphs, links, labels, button titles and others.
But the colors seem very similar to Fill. Why do we need an additional semantic group? The thing is that texts are thiner then the icons or interface controls. Therefore they look lighter even when they have absolutely same color.
If we want to maintain the ability to manage text colors separately and adjust them slightly, we need separate tokens for them.
Layers are the simplest yet trickiest aspect. There are few tokens and only 2–3 colors, but the structure of these tokens is very important, especially in dark mode.
We should imagine our interface in the 3D prospect and decide how many layers we should have.
At Aloha, we have 3 floors and 1 overlay. In light mode, the floors use just 2 colors, but in dark mode, the interface looks deeper.
In the Stroke section, we have colors for element borders and dividers. You might need brand colors, positive and negative colors, as well as a couple of light grey colors.
Yes, the Stroke palette looks very very similar to the Fill palette.
In most cases, you will find all the necessary colors for your strokes in the Fill palette. It seems redundant to create another palette with exactly the same colors. At Aloha we decided to use Fill palette for both: fills and strokes.
But sometimes, you might want the same color used in light mode to behave differently for fill and stroke in dark mode.
So, on one hand, it is easier to use the same palette for fills and strokes. You don’t have to think about which palette to apply. But from the other perspective, two palettes give you more flexibility and more clarity about how to use specific colors.
We have talked about semantic tokens, which can be used in different color themes. Each token has a specific function in the interface. But what if there are colors that are the same in both light and dark modes? Usually, these are shades of black and white.
For these colors, we should have a separate palette. Let’s call it the Static Palette. Add colors that are not affected by the color mode. Each static token also refers to colors from the Core Palette.
To summarize… Having a streamlined Semantic Layer allows you to expand the color palette for your interface as much as you like while maintaining a clear and accurate structure.
Creating a consistent color palette for your interface