XP Investimentos, Clear Corretora, XP Inc.
On an investment screen, misreading a number costs money. So Clear’s colour was built from contrast, not checked against it afterwards.
Result
On the final screens, all text passes WCAG level AAA, the international standard for digital accessibility. Contrast is measured as a ratio between the brightness of the text and that of the background. 4.5:1 is the accepted minimum and 7:1 is the highest level.
Primary text at 14.61:1, secondary at 9.21:1, status labels between 7.09:1 and 12.62:1. One single exception, declared. In the previous palette, chart labels sat close to 3.5:1, below the minimum.
Scale
84 shades
across 7 families, in each of the two modes
Contrast
AAA
on all text, with one declared exception
Regularity
2× more
regular from one shade to the next
Problem
The minimum and maximum labels on the chart sat close to 3.5:1 on white. In a brokerage app that is not a question of style. It is a share price made unreadable for anyone with low vision.
Swapping that grey would have fixed one screen and nothing else, because the problem was the whole scale. The lightness gap between one shade and the next ranged from 0.031 to 0.144, almost five times apart. Anyone picking shade 600 expecting one contrast got another, and every screen became a new negotiation.
There were three families, primary, secondary and neutral, for a product that has to say price up, price down, warning, error and information.
Decision
HSL is the industry standard and it gets lightness wrong. With the same recipe, 60% saturation and 40% lightness, the green lands at 3.25:1 on white and fails. The red lands at 7.23:1 and passes the highest level. The number is the same, the brightness the eye sees is not.
I chose OKLCH, a model that describes colour by lightness, chroma and hue following how the eye perceives it, not how the screen emits light. In it, the scale stops being a collection of picked colours and becomes a function. Shade 600 has a lightness of 0.61 across all six colour families. That is a measurement, not an approximation.
The steps from one shade to the next now range from 0.034 to 0.081. Twice as regular as before.
Same recipe, two models
HSLsaturation 60% · lightness 40%
OKLCHlightness 0.45
In HSL, the same lightness value produces different brightness. In OKLCH, the same lightness looks the same to the eye.
Method
Checked at the end, contrast turns into rework. Pick, test, fail, start again. Used as the starting point, it becomes a guarantee.
I calculated the cross matrix of every text and background pair in the system and turned the result into usage rules. Which shades take text, which do not, and from what text size the answer changes. Designers do not need to measure. They need to follow.
Matrix excerpt · text on background, light mode
| Background | Subtle background | Sunken background | Primary action | Up, fill | Down, fill | |
|---|---|---|---|---|---|---|
| Primary text | 18.44 · AAA | 17.81 · AAA | 16.78 · AAA | 2.31 · fails | 2.61 · fails | 2.33 · fails |
| Secondary text | 9.50 · AAA | 9.17 · AAA | 8.64 · AAA | 1.19 · fails | 1.34 · fails | 1.20 · fails |
| Tertiary text | 7.83 · AAA | 7.56 · AAA | 7.12 · AAA | 1.02 · fails | 1.11 · fails | 1.01 · fails |
| Accent | 8.00 · AAA | 7.72 · AAA | 7.28 · AAA | 1.00 · fails | 1.13 · fails | 1.01 · fails |
| Up, text | 9.03 · AAA | 8.72 · AAA | 8.21 · AAA | 1.13 · fails | 1.28 · fails | 1.14 · fails |
| Down, text | 7.91 · AAA | 7.64 · AAA | 7.20 · AAA | 1.01 · fails | 1.12 · fails | 1.00 · fails |
| Text on colour | 1.00 · fails | 1.04 · fails | 1.10 · fails | 8.00 · AAA | 7.07 · AAA | 7.91 · AAA |
Trade-off
Green for up and red for down is the strongest convention in financial markets and one of the worst for accessibility. It is the classic case of colour blindness, which affects around 8% of men. Here the mistake is not aesthetic, it is someone’s balance.
Dropping the convention was not an option, because traders read the colour before the number. The way out was never to leave colour on its own. The plus or minus sign always sits right next to the number, and on charts each series gets its own line style and marker.
This is where OKLCH paid for itself. The green and red for up and down sit on the same shade of the scale, 800, with the same lightness, 0.45. The down line does not look heavier than the up line for no reason. The cost of that choice is that, without hue, the two become the same tone. The sign stopped being a detail and became a rule.
Text
Fill
Up text at 9.03:1 and down text at 7.91:1 on white. The sign comes before the number, always.
Text
Fill
Deuteranopia simulation, one of the forms of colour blindness that confuse green and red. Both colours turn olive and the sign still says it all.
Up as a solid line with circles, down as a dashed line with squares. It still reads when printed in black and white.
Limit
The previous palette lived on the edge of what the screen can show, between 95% and 100% of the sRGB limit, the range of colours an ordinary screen can display, on most shades. Colour in that region is unstable. It saturates, loses the difference between neighbouring shades and changes from one device to the next.
The new scale follows a chroma curve, the intensity of the colour, running from 28% to 88% of that limit. It rises to the middle of the ramp and drops at the ends. The very light and very dark shades give up intensity to keep lightness predictable. The system loses a few vivid shades at the extremes and earns the right to be used by rule.
HOW MUCH OF THE sRGB LIMIT EACH SHADE USES
Decision
No. Highly saturated colour on a dark background seems to vibrate and lift off the surface, an effect that tires the eye quickly during long reading. Those colours were left out of dark mode, with the Atlassian design system as the tonal reference. The dark surfaces went from three levels to four.
Instead of redefining the same variables depending on context, I built two complete sets of primitives, the raw colours the whole system refers to. One for light backgrounds, one for dark. The alternative would have made the file leaner and broken the handoff to development, because Figma’s Dev Mode loses track of a value that changes meaning depending on where it sits. A smaller file is not worth a worse handoff.
Dark mode surfaces · before 3, now 4
Background #0D1116
Subtle background #14171E
Raised #1B1F26
Overlay #24282F
Limit
Six chart series distinguishable from each other at 3:1 would require 3 to the power of 5, that is 243:1 between the extremes. An ordinary screen offers around 21:1. No palette can solve that, and a system that promises to is getting its sums wrong.
The rule that stuck: each series has at least 3:1 against the background, and the difference between series never depends on colour alone. Line style, marker and label do the rest, as the standard itself requires.
Result
A living document in eight sections, the kind a team looks up instead of asking. Properties, palette, typographic accessibility, a token matrix at AAA level, contrast for non-text elements, focus appearance, chart series and the migration guide from the previous version.
In Figma, 224 variables in three collections. Primitives with the raw colours, semantic tokens with the meaning of each use, such as background, text, up and down, and components. Two modes, light and dark.
The declared exception is the placeholder text in the search field, at 5.75:1. It passes AA but not AAA. It is documented as an exception instead of being rounded up, because a system that hides its own exception stops being verifiable.
8 sections
in the living document the team looks up
224 variables
in 3 collections and 2 modes in Figma
67 checks
of palette contrast, no AAA failures
1 exception
declared, the search placeholder at 5.75:1
Learnings
When the rule comes from the measurement, nobody has to measure again. Checking at the end only finds the mistake later.
A 5.75:1 written into the document is more trustworthy than an AAA everywhere that nobody can verify.
Colour blindness and chart series had the same way out, a second channel. Sign, line style and marker.
Colour system and accessibility verification, mine end to end, built on the foundation of Clear primitive tokens I had created in SOMA.
XP Investimentos, Clear Corretora, XP Inc.