Automatically creating an accessible color palette from any color? Sure!

How do you let your users chose any color, but still make sure that the colors make your app useful and accessible? You create rules.

Ida Aalen
Ida Aalen
Dec 7, 2017 · 6 min read

Find even more details and resources here:

To pick or not to pick a brand color

Should we really let them make this choice?

But it’s easier said than done. One brand might have a dark blue, another one a pale pink. Would you be able to read the text on a dark blue button? And would a pale pink button stand out as a call to action?

Letting users choose any color opened for a lot of issues, but it also made it clear that we wouldn’t be able to guarantee that the colors used in the interface had a sufficient contrast to be accessible (ast least 4.5:1 to fulfill WCAG AA). Not only am I convinced that striving for accessibility is the right thing to do — in Norway most of the WCAG AA criteria are enforced by law for any site or app directed at the general market.

So what do you do? Well, put a graphic designer (Eivind Molvær), a developer (Dag-Inge Aas), a designer/frontender (Jørgen Blindheim) and a UX designer (me) in a room and let them agree on some steps and rules.

Step 1: Get to know CSS Custom Properties

body {
--color-primary: tomato;
background-color: var(--color-primary);

We are also able to set these properties from JavaScript easily, making it possible to create powerful theming engines that can change whole color schemes on the fly."--color-primary", "tomato");

With these tools in hand, we set out to create our solution.

Step 2: Black or white button text?

// in Theme.js
import Color from "color";
const primaryColor = Color(brandColor);
const primaryColorContrast = primaryColor.light()
? Color("black")
: Color("white");"--color-primary", primaryColor.string());"--color-primary-contrast", primaryColorContrast.string());
// in Button.css
.button {
background-color: var(--color-primary);
color: var(--color-primary-contrast);
Yellow button? No need to change the color of the button text here :)
Dark blue button? White is a lot more legible than black!

Step 3: Generate lighter and darker color variants

To create the lighter version, we mix 20% of the brand color with 80% white. To create the darker version, we mix 80% of the brand color with 20% black, and then we check to see if it validates against white. We keep adding 10% black until the new darker color validates against white.

import Color from "color";function getValidatedColor({
colorToValidate = Color("white"),
minimumContrastRatio = 5,
tries = 0,
maxTries = 8,
}) {
const newColor = colorToChange.mix(mixingColor, mixingAmount);
if (
newColor.contrast(colorToValidate) < minimumContrastRatio &&
tries < maxTries
) {
return getValidatedColor({
colorToChange: newColor,
mixingAmount: 0.1,
tries: ++tries,
return newColor;
const primaryColor = Color(brandColor);
const primaryColorLight = getValidatedColor({
colorToChange: primaryColor,
mixingColor: Color("white"),
colorToValidate: Color("black"),
mixingAmount: 0.5,
const primaryColorDark = getValidatedColor({
colorToChange: primaryColor,
mixingColor: Color("black"),
mixingAmount: 0.2,
// then set these as CSS Custom properties

This way, you end up with automatically generated palettes like these:

Automatically generated color palette. The lighter version, the brand color, and the darker version.

Step 4: Extra outline on buttons?

// in global stylesheet
body {
--button-border: none;
// In Theme.js
import Color from "color";
const primaryColor = Color(brandColor);
if (primaryColor.contrast(Color("white")) < 5) {
"2px solid var(--color-primary-dark)",
// in button.css
.button.primary {
background-color: var(--color-primary);
border: var(--button-border);

This way, if the primary color validates, the button border will be “none,” and if it doesn’t validate, JavaScript updates the Custom Property to set a border value on the button. This then gets updated in realtime for all buttons on the page.

Thanks to the darker outline, this button is visible even though the brand color is a pale pink.

Step 5: Secondary colors with hue rotation

Rotating the turquoise brand color 60 degrees gives us a nicely matching blue color so that the top message in this form stands out from the call to action button.

But having lighter and darker versions of the brand colors wasn’t enough.

When working on the design for elements like warnings, error messages and notifications, we realized we needed a color that stood out from the brand color. But where would we find that?

By rotating the brand color 60 degrees, we could generate colors that matched the brand palette, but were was also different enough that they would stand out:

const noticeColor = primaryColor.rotate(60);

Try it yourself!

Why should you care?

Dag-Inge (CTO) & Eivind (designer)

Originally, I thought of this as a specific and limited problem. But thinking about it, this applies to any scenario where users can pick their theming colors (for instance in your Twitter profile).

It seems the thinking these days is that when we let users pick colors, the user is responsible for any contrast issues.

I don’t think that’s fair to neither the user picking the color nor the end users. A sentence like “but does the color validate against white?” is meaningless to most people.

Making sure our interface has an accessible color scheme is our job.

We would love to hear ideas for how to improve the system and any other thoughts in the comments below :)


We're building video software tailor-made for professionals…

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store