Graphic design

Colour theory, the working version

Colour theory is a small set of rules describing how two colours relate to each other and why one of them can be read on the other while the second cannot. Every colour is three numbers, hue, saturation and lightness, and of those three only lightness decides whether text on a background is legible.

  • Lesson 3 of 12
  • Beginner
  • Free, no signup

One full turn of the wheel, from the base colour to its complement and back

Every stop is one relationship. The further you walk from the base, the more tension you get.

  • 1 The base colour
  • 2 Analogous
  • 3 Triadic
  • 4 Complementary
  • 5 Triadic, the other side
  • 6 Analogous, the other side

The node colours mark each stop position on the wheel, not the hue itself; they come from this site own palette. And the figure speaks only of hue: lightness and each colour share of the screen sit outside it.

Last checked: Facts and tool names in this lesson are re-checked against their sources on this date.

A colour is not a name, it is three numbers

When you say "blue", a thousand blues are possible. To reason about colour and make decisions, you have to leave the name behind and get to numbers. The system most designers think in has three of them and is called HSL.

Colour theory, in practice, means working with those three axes. Hue is the colour position on the wheel, expressed in degrees from zero to three hundred and sixty. Saturation says how pure that hue is; zero saturation is grey. Lightness says how close it sits to white or to black.

The benefit is that you can move one axis without disturbing the others. To make a lighter version of a brand colour you leave hue and saturation alone and raise lightness. A hex code gives you no way to do that, because hex only states the amount of red, green and blue and offers no handle for "slightly lighter". CSS understands hsl() natively, so nothing needs converting by hand.

One point belongs here at the start, because it matters later and is rarely written down: lightness in HSL is not the lightness your eye sees. Yellow at hsl(60 100% 50%) and blue at hsl(240 100% 50%) both carry fifty percent lightness, yet that yellow gives a contrast ratio of 19.56:1 against black and that blue only 2.44:1. One identical number, two colours the eye does not read as remotely equal. Newer colour spaces such as oklch(), which arrived in the fourth CSS colour specification, exist precisely to fix that flaw.

The three numbers that make one colour

Each can be moved without touching the other two; that is what makes HSL more useful than hex.

Hue, the position on the wheelSaturation, how pure
One colourthree independent axesLightness, how close to white

HSL lightness is not the lightness the eye sees, and the example is in the text of this section. Work that needs perceptual lightness belongs in a newer colour space.

What the colour wheel says, and what it does not

The colour wheel arranges hues around a circle, and its job is to make relationships visible. Three of those relationships do most of the work: complementary, two colours opposite each other; analogous, two or three neighbouring hues; and triadic, three points spaced evenly around the circle.

Each carries a different feel, and the feel is not mysterious. Neighbours are calm because the eye makes no jump between them. Complements are tense because they sit as far apart as the circle allows, which is what makes them good for a primary button and bad for body text. Triads land somewhere between the two, and they work better than either on data-heavy screens such as charts, because they keep three categories apart.

Now for what the wheel does not say, which is where most palettes break. The wheel speaks only about hue. It says nothing about how much of the screen each colour should take, nothing about how light each one should be, and above all nothing about which of the three relationships belongs to your project. A complementary palette that is flawless on the wheel can be a disaster on a real page, because every decision that matters is taken outside the circle.

The cultural meaning of a colour also sits outside the wheel. What a colour signals in the Iranian market, or what it leads people in a particular industry to expect, has nothing to do with geometry and deserves its own treatment; colour psychology in branding opens that argument separately.

How many colours should a palette have?

The short answer: fewer than you think. Palettes that survive contact with real work usually carry one primary hue, one supporting hue, one accent used sparingly, and two or three greys. That is the whole list. Most of what else you see in a palette is lightness steps of those same few hues, not new hues.

That last sentence matters more than the hue count. What holds an interface together is not the number of colours but the number of lightness steps: the page background, a card surface, a card on top of a card, a border, body text, secondary text, dimmed text. Without those steps the screen stays flat no matter how many colours you add.

The familiar 60-30-10 rule of thumb says the same thing in terms of area: roughly sixty percent of the screen in the dominant colour, thirty in the support, ten in the accent. As a starting point it is fine, but it breaks in two places quickly. First, it talks about area rather than importance; the accent takes the smallest share and does the most important job. Second, on a data-dense dashboard or a table those proportions mean nothing, and what replaces them is a stricter rule: colour only where it carries meaning.

Our position, after years of building Persian interfaces, is this: most brand palettes have too many hues and not enough greys. If you are going to add more of one thing to your palette, add greys.

The five roles a working palette fills

  • The dominant colour

    The largest share of the screen. If the brand has a colour, this is usually it.

  • The supporting colour

    For sections that must separate from the rest without shouting.

  • The accent

    The smallest area and the most important job. Put it everywhere and it stops being an accent.

  • The light neutrals

    Surfaces, borders and spacing. Seen more than the primary colour ever is.

The palette

These are roles, not a colour count. One hue can fill several roles through different lightness steps, and usually that is the right move.

Why contrast is the work of lightness, not colour

This is the one part of colour theory with a hard number behind it, and it is also where the most mistakes happen. Take pure red on pure green: two entirely different colours, as opposed on the wheel as many people imagine colours can get, and their contrast ratio is only 2.91:1. Red text on a green background is effectively unreadable, however opposite the two may look.

The reason is simple. The human eye reads differences in lightness, not differences in hue. A hue difference creates mood; a lightness difference creates legibility. Confuse the two and you build a palette that is superb in a swatch strip and unreadable on a real screen.

Here is a test that takes twenty seconds and is the most useful thing in this lesson: turn the design greyscale. If, in grey, you can still tell what matters and the text still separates from its background, your palette is sound. If everything collapses into one uniform grey, you had colour but no contrast.

The required numbers come from WCAG: at least 4.5:1 for ordinary text, and 3:1 for large text and interface components. We open those up in the web accessibility lesson with the real numbers from this site palette and the way to measure them, so they are not repeated here. What this lesson adds is one thing: contrast is not a property of a colour, it is a property of a pair. A colour that is excellent as text may fail when it becomes the background itself, and at that point you need two versions of one hue rather than one.

Which axis makes contrast and which only makes mood

Both are needed. The mistake is expecting the first job from the second axis.

Lightness: what makes text legible

  • Text on a background, anywhere on the page
  • The border of an input field and any icon that carries meaning
  • Surface steps: background, card, card on card
  • Anything that must survive the greyscale test

Hue and saturation: what makes mood

  • Brand character and how it differs from a competitor
  • Whether the whole page reads warm or cool
  • Separating categories in a chart, alongside a second cue
  • A sense of success or danger, when the text says it too

This split is about reading text. For separating categories in a chart, hue genuinely does work, provided it is not the only cue.

One hex code, two screens, two colours

Something colour theory tutorials almost never mention: a colour code is an instruction, not a guarantee. The same hex becomes three slightly different colours on the designer monitor, on the client cheap phone and on the projector in the meeting room, and that is nobody fault.

The reason is colour space. For years everyone assumed sRGB and a colour code meant something inside it. Many displays now cover a wider gamut, and the fourth CSS colour specification lets you write colours that do not fit inside sRGB at all. The consequence is that a "brand colour" is no longer a fixed point; it is a range.

Print is a third world again. A screen works with light and paper works with ink, so the highly saturated, glowing colours that catch the eye on screen turn into a dullness in print that the client did not expect. If you are building a brand identity that has both a screen and a business card, look at the palette on both, not only on a monitor.

What we do is simpler than it sounds: we look at the primary colour on a cheap phone in daylight, not only on a calibrated monitor in a dark room. Colours that are only good under perfect conditions are not good in practice.

The fast path, with AI

What models are genuinely good at in palette work is not generating combinations, because any palette generator does that. The valuable part is this: take a colour you already have, build its lightness steps, compute every pair that will carry text on a background, and say plainly which pairs fail. Put the model in the role of the calculator, not the artist.

  1. Take the colour you already have, even if it is only one hex from the logo. Starting from the brand colour ties the palette to what the client owns from day one.
  2. Run the recipe below with that hex. A fast, cheap model of the Gemini Flash class is enough here, because the work is mostly arithmetic; our current pick among models is kept up to date in the <a class="text-link" href="/ai/">AI section</a>.
  3. Read the pair table from the top and fix every row below the threshold right there by raising or lowering lightness. Leave hue alone; change the hue and it is no longer the brand colour.
  4. Put the output on a real page and turn it greyscale. Correct numbers are necessary and not sufficient; the greyscale test is what actually decides.

Copy-ready recipe

My base colour is: {brand hex}
Site background: {light or dark}

1. Build nine lightness steps from this colour, from very light to very dark, keeping hue and saturation roughly constant. Write each step as a hex code and as its hsl() equivalent.
2. Also propose three greys that sit well with this hue: one for a surface background, one for borders, one for secondary text.
3. Build a table of every pair that will in practice be text on a background. For each pair compute the contrast ratio with the WCAG relative luminance formula and write the number to two decimal places.
4. Beside each row write pass or fail, against 4.5:1 for ordinary text and 3:1 for large text and interface components. Put the failing rows first.
5. For each failing row propose exactly one correction, and that correction must be a lightness change, not a hue change.

Do not add any colour of your own to the palette. If answering needs something I have not written, ask; do not guess.

Before you trust the output: Two things this recipe does not solve. First, the numbers the model gives should be checked once by you; contrast arithmetic is simple, but models do make mistakes in hand calculation, and the colour picker in the browser developer tools shows the same number free and correct. Second, none of these numbers says your palette is beautiful or right for the brand; they say it can be read. That judgement is yours, and the only way to make it is putting the palette on a real page rather than in a swatch strip.

AI in this kind of work

Our position on colour and AI: excellent for calculating, not for choosing. Models compute contrast ratios faster than you do, build lightness steps and pull the existing colours out of a photograph. But "which colour suits this brand" is a question about a market and its customers rather than about geometry, and there the model answer is an average of what it has seen, which is exactly what makes everyone output look alike.

Tools that actually help

  • Gemini The best fit for the recipe above, because it both reads an image and does arithmetic: hand it a screenshot of the current page and ask for the pair table. Google own page says the Gemini web app is available in more than two hundred and thirty countries and territories, and Iran is not on that list; we read that on their page rather than measuring it.
  • Claude Better when you want the palette turned into code: CSS variables with meaningful names, lightness steps as tokens, and a light and dark mode side by side. Iran is on neither of Anthropic two supported-countries lists, which we read on Anthropic own page.
  • Adobe Color Not AI, and that is its merit: the wheel, the complementary, analogous and triadic relationships, and a separate section for contrast and colour-blindness checks. For learning the relationships themselves it is clearer than any model, because you see the result as you move. Whether it is reachable and payable from Iran is something to test yourself; we write no claim about it.

Where it backfires

The main risk here is neither security nor copyright but something simpler: a palette a model builds gets judged in a swatch strip rather than on a page. Palette generators, AI or not, optimise hue relationships and not contrast, which is why a beautiful palette keeps arriving at text that fails the WCAG 4.5:1 threshold. Any palette you take from a model has to be computed pair by pair before you use it.
The second risk is more general and has nothing to do with colour: any image or brand file you put in a chat has left your machine, and what happens to it depends on the plan and the settings of that service and is written on its own data-usage page. For a personal project that is no issue; for a client visual identity that has not been revealed yet, get permission first. To see how each of these tools can be paid for from Iran, see the buying guide.

Sources: W3C: Understanding Contrast (Minimum), WCAG 2.2 Google: where the Gemini web app is available Anthropic: supported countries Adobe Color: accessibility tools

Where this advice stops

This lesson is about colour on a screen, and nearly everything in it assumes the light comes from the display itself. For print the equation changes: the colour space is CMYK, highly saturated colours are simply not printable, and paper and ink affect the result on their own. The second boundary is colour blindness; sufficient contrast is necessary, but for someone who cannot separate red from green no colour combination is enough on its own and a second cue is required. And a third boundary worth stating plainly: nothing in this lesson tells you which colour is right for your brand. Colour theory says how two colours relate, not which one you should choose.

From our own work

Every colour in every figure on this site comes from one place, and that place is a four-stop gradient: #ff4d6a, then #b06bff at thirty five percent, then #4d9fff at sixty eight percent, and #2ee6a8 at the end. The rgb_dg_hue() function in the theme diagram engine takes the colour of node i out of n by sampling that same gradient at i divided by n minus one. The consequence is that colour in a figure is not decoration but a display of order, and the whole section runs on one colour definition instead of dozens of manual picks.
And here is the lesson we paid for while building those charts, visible in the same code: the method lies when the figure is a verdict. If the rows are ordered from good to bad, positional hue also colours a category that sits outside that scale as though it belonged on the spectrum. So the charts gained a hue key on each item that pins the colour to the category and stops it coming from a position in a list. Colour means something when it is attached to meaning, not to a row number.

Real follow-up questions

To start out, should I learn hex or HSL?

You read hex and you write HSL. The colour code you get from a logo or from a designer is almost always hex, so you need to recognise it; but when you build a palette yourself HSL makes the work simpler, because you can move one axis on its own. Converting between them is one line of CSS and needs no tool.

The client brand colour does not have enough contrast. What now?

Do not change the brand colour, change its role. Keep the same hue at two different lightnesses: the original for places where the colour is only seen, and a darker or lighter version where text sits on it. That is exactly what we did with this site palette, shown with the numbers in the accessibility lesson.

Is dark mode just the same palette inverted?

No, and a straight inversion usually gives a poor result. Highly saturated colours read as brighter and harsher on a dark background, so their saturation has to come down a little; and pure white on pure black is tiring for long reading, which is why most dark designs use a slightly grey white and a slightly blue black. In practice dark mode is a second palette, not the first one flipped.