Adaptive, accessible and automated colour for Design Systems

Calibrating contextual colours to be readable and recognisable anywhere.

Lucas Zhang
Societe Generale Design


The creation of our Design System’s colour palette was like many others; we began by auditing and then standardising a set of functional colours by how they were used generally, while assuming we could manage niche-cases with hot-fixes here and there along the way.

This went well until the UI elements we were dealing with started getting trickier…

As the complexity of our user interfaces evolved, it became obvious that the static colour system wouldn’t be sustainable nor flexible enough for our design vision, especially with the struggle of having enough colour-contrast to reach accessible standards.

This is why, 8 years since our initial audits, we embarked on a several month-long journey in 2019–20 to make our colours not just readable, but recognisable wherever they are, while keeping things easy for designers and developers.

Example of reality and expectation before we tackled the subject.

Navigating through digital colours

Not all computer monitors/screens can simulate true colour tone due to the physical limitation of resolution, brightness and contrast ratio, alongside a lot of other factors that might affect the colour perception when transcoded into digital format.

Example of the scale of theoretically visible colour chroma under RGB model.

Even if we were, let’s say, to use the absolute highest saturation for each colour hue for “contextual” colours, they would have significant inconsistencies when displayed alongside one another.

Colour models like RGB, HSL, HSV or HSB can confuse us by compiling the available colour space into numerical formats (0–100, 0–200, 0–255), where you might expect the max value of one colour to be the same as the max of another, while in fact, each hue has quite a different chroma (the strength of the colour) depending on its lightness.

Comparison of ideal colour space and true colour space. (The illustration above are created to reflect Munsell chroma units that been referenced in Atlas of the Munsell Color System.)

This chromatic inconsistency leads to hues contrasting irregularly when on other background colours.

The graph below shows how some of the colour hues perform on a white background (#FFF):

Example of colour hues performance of the WCAG contrast ratio on a light background.

As you can see, blues and reds really keep their recognisable chroma far above their standard lightness. Yellow, green and cyan on the other hand have a much narrower range where they can still be perceived as intended. Combining that with the overall lower lightness levels needed to achieve sufficient contrast to meet accessibility guidelines (using the WCAG as reference), it becomes very tricky to choose recognisable colours.

This situation is almost reversed on dark backgrounds — the yellows, greens and cyans are now very visible, completely maintaining their chroma, while red loses its redness and blues have to be washed out to meet the 4.5 contrast ratio.

(By the way, this is why static colours can’t just be inverted for dark themes!)

Example of colour hues performance of the WCAG contrast ratio on a dark background.

Capturing the colour we know

Now that we’ve considered the technical factors, let’s focus on four common contextual indications in Design Systems: Success, Information, Warning and Danger.

By following the common perceptions and culture reference of colours and their associated contexts (Green = Positivity, growth, success; Red = Negativity, urgency, danger;), we can begin to select four colours that imitate real-life contextual indications:

Perceptual colours in real life.

Danger red, Success green, Warning orange and Info blue.

With these colour-concepts in mind we might start by choosing the richest possible RGB values for all four:

The RGB “contextual” colours.

Now you might ask, from here why can’t we just pick a lightness for all our contextual colours that meets accessible contrast ratios? Wouldn’t this lead to a far more consistent looking feel?

It is true that a universal colour luminance can give adequate chroma and contrast consistencies. However, as our colours need to have that “richness” to keep their meaningful recognition, we need to calibrate a balance between the readability (given by the contrast with the background) and recognisability (given by the chroma).

One particularity tricky case was a Success green. It had to be vibrant enough to represent the idea of positivity in general, but as we’ve seen greens completely lose their vibrancy when trying to achieve contrast on other brighter backgrounds.

So, in order to select an accessible green, there had to be a compromise on the vibrancy one way or another. The first part of the solution was to gently adjust the hue towards the much more contrastable “Blue” (240) by approaching “Cyan” (180) on the spectrum, then the second part was then to turn down the lightness until it hit the WCAG contrast ratio:

Example of the adjustment of our design system’s positive $success green.

The next difficulty was creating our Info blue. As much as the max RGB “Blue” (240) is already recognisable and accessible as is (on light backgrounds), all it needed now was less intensity and a more standard variant that can pair well with other contextual colours.

As it happened, the solution was almost the opposite of our green method. Adjusting the hue toward the “Green” colour spectrum while increasing the lightness — resulted in a similar luminance to its peers.

Example of the adjustment of design system’s informative blue $info.

Not one, but many…

So by this stage, we’ve managed to choose recognisable and readable hues for our contextual colours on a white background. But what about all the different backgrounds that they need to be displayed on?

Colour structure

We created variable colour attributes for each contextual colour, that adjust colour tone and lightness depending on the immediate background colour. While also preserving the all-important contextual meaning of the colour.

The work started with the base palette — four colours, on each of our four background shades:

Contextual colours on UI background shades.

But this alone wasn’t enough; compared to the usage on backgrounds, visibility is the priority when applying colour to text and symbols. As the nature of the elements that would actually be using these contextual colours are few and far between, to be seen comfortably a little additional contrast is needed when working with a variety of backgrounds.

The solution we went for is the use of “Sass functions”. These are commonly used to define complex operations or variables that can be reusable throughout CSS stylesheets, so we used them to modify the HSL attributes and compile presets, which pushes that extra chroma or adjusts the lightness to sustain contrast especially over our smaller text sizes. Here’s the result on our typography system:

Lightness adjustment by font’s size.

Larger text sizes like page titles can still use the base colour as intended, while the colour on smaller size text will be darkened or lightened as needed to give additional visibility.

Some of our colours need this added contrast more than others (of course it wasn’t going to be simple…). For example, Warning orange starts to reduce 8% of its lightness on light backgrounds when the font size becomes less than 24px, while on dark backgrounds Danger red will be adjusting 2° hue and raise 3% of its lightness for all text smaller than 24px.

And so, adding the magic (or automatic execution)

Now, at this point you might be thinking, your designers and developers have to modify colours for each background and font?! Although it was definitely an option, we really wanted to keep our colour system as intuitive as possible — after all, we only want our users to think of four colours, so why can’t our designers and developers do the same?

Here’s where our automated “magic” comes in. We set up our system to automatically detect the background colour, font size and other factors that influence the colour as we defined, and then adjust the element’s colour automatically! There are a few ways of achieving this, including detecting the colour and background with a script, but we preferred not to add more JS to load, and instead to crunch through all the colours in our SCSS.

Where the magic happens?

The end result? Designers, developers and users can now think of our contextual colours more as token, not needing to worry about all of the subtle changes of the actual colour required to be to hit our goals of readability and recognisability. For those using our colour system, Success green is just a codename — when in reality it could be one of 20 or more carefully selected colours, depending on the context.

Wrapping up

So here we are. This whole process eventually gave us a robust system for contextual colours, as well as some operational colours to which we applied the same concepts to. It also leaves us with a neat framework to expand the colour palette in the future (…theming for colour blindness for example!).

More profoundly, it shows more of the true nature of colour; unique, organic and incredibly diverse — which at the end of the day is what influences people in their colour perception in the first place.

Additional editors: Henry Daggett and Morgane Peng

This article is part of our Design System series: a 5-year overview, 4 false assumptions, getting buy-in, governance & chaos (part 1), governance & chaos (part 2) and surviving. Watch this space for more!