One brand colour, a working system
Brand, neutral and status ramps with every semantic role measured against the surface it actually sits on.
One brand colour, a working system
Brand, neutral and status ramps derived from your colour, with every semantic role measured against the surface it sits on.
Semantic roles, measured
How the roles are derived
Primitive ramps first, semantic roles second. A role is a reference to a step, not a separate colour, which is why changing the brand colour moves the whole system coherently.
Hover and active are not fixed percentage darkenings. They are OKLCH lightness deltas, gamut-mapped, then checked for whether the states are actually distinguishable from each other. A 10% RGB darken on a light colour is invisible and on a dark one is a different colour.
Status hues are fixed — green for success, amber for warning, red for danger — because those associations are conventional and inventing new ones costs comprehension. Their chroma and lightness are matched to the brand's so they belong to the same system.
Dark mode is generated from the same primitives with different semantic mappings, not by inverting the light theme. Inversion produces glaring surfaces and text that is too bright.
The neutral is the hardest part
Pure grey next to a saturated brand colour looks dirty. Most well-made systems tint the neutrals very slightly toward the brand hue — a chroma of around 0.01, far too little to name as a colour and enough to make the greys sit correctly.
Hover states need to be visible
A hover that changes lightness by two points is a hover nobody perceives. This checks that each state is far enough from its neighbour to be seen, and says so when it is not.