Guide

How to use color codes in CSS

Every way CSS accepts a colour, and the two features that mean you should almost never repeat a hex code.

Last updated 25 August 2026

CSS will take a colour in about a dozen notations, and they are all equally valid. The more useful question is where to write it, because a stylesheet with the same hex code in forty places cannot be rebranded.

The notations, in one place

FormExampleUse it for
Hex#3498dbCarrying a value from a design tool
Hex with alpha#3498db80Translucency without switching notation
rgb()rgb(52 152 219 / 50%)Channel arithmetic, or when a value comes from code
hsl()hsl(204 70% 53%)Hand-adjusting lightness or building a ramp
oklch()oklch(62% 0.13 240)Perceptually even ramps, wide-gamut colours
KeywordtomatoPrototypes and debugging only
currentColorborder-color: currentColorAnything that should follow the text colour
transparentbackground: transparentExplicitly no colour

Note the modern space-separated syntax: rgb(52 152 219 / 50%) rather than rgba(52, 152, 219, 0.5). The comma forms still work and always will, but the space form is what current specs use, and it removes the need for a separate rgba() function.

Custom properties are the actual answer

A hex code in a rule is a value. A hex code in a custom property is a decision you can change later. The difference compounds fast.

The mistake most people make on the first attempt is naming properties after the colour:

--blue: #3498db; works until the brand blue becomes green six months later. Then you have a property called --blue holding a green, or a rename touching two hundred files.

Name them after the job instead. Two layers works well:

Layer one, the ramp. Literal values, named by family and step: --blue-500: #3498db. These are ingredients and are never used directly in a component.

Layer two, the roles. Aliases that say what a colour is for: --text-body, --surface-card, --border-subtle, --action-primary. Components only ever reference these.

The payoff is that a theme becomes a small block of overrides. Dark mode leaves the ramp alone and reassigns the roles:

:root { --surface-card: var(--grey-0); }
[data-theme="dark"] { --surface-card: var(--grey-900); }

Every component that used var(--surface-card) is now theme-aware and you did not edit any of them. This site is built exactly this way.

Relative colours replace most colour functions

CSS can now derive one colour from another, which used to require Sass. The relative colour syntax reads a source colour and lets you rewrite individual channels:

--hover: oklch(from var(--brand) calc(l - 0.06) c h);

That says: take the brand colour, drop its lightness by six percent, keep its chroma and hue. It updates automatically if the brand colour changes, and it works with runtime values that a preprocessor could never see.

color-mix() covers the other common case, blending toward another colour:

--tint: color-mix(in oklab, var(--brand) 12%, white);

Mix in oklab rather than the default sRGB. Sass's mix() worked in sRGB, which is why its midpoints often came out muddy. The colour mixer shows the two side by side.

Alpha, and the trap in it

Using opacity for a muted text style is tempting and usually a mistake. Text at rgb(0 0 0 / 60%) has a different contrast ratio on every background it might land on. If the component appears on both a white card and a tinted surface, it passes in one place and fails in the other.

Prefer a solid colour from your ramp for text. Reserve alpha for things that genuinely need to show what is behind them: scrims, overlays, hover washes, focus rings.

Also keep opacity and a colour's alpha distinct in your head. opacity applies to the whole element including its children and creates a new stacking context; a colour's alpha applies to that one paint operation.

A short list of things worth doing

  • Declare colours once, as custom properties, in two layers.
  • Name roles, not hues.
  • Use currentColor for icon fills and borders that should follow the text.
  • Set color-scheme: light dark so form controls and scrollbars follow your theme.
  • Mix and interpolate in oklab.
  • Check the pairs, not the values. A ramp that looks tidy can still fail contrast, so run the combinations you actually ship through the contrast checker.

If a colour is currently trapped in an image or a screenshot, the image picker will get you the exact value, and the converter will give it to you in whichever notation your stylesheet uses.