The Colour That Isn't There
You set a semi-transparent overlay: rgba(0, 0, 0, 0.5) over a white background. The result looks grey. You want to hardcode that grey, so you open a colour picker, sample the rendered pixel, and get #808080.
Then you use #808080 somewhere else, over a different background, and it doesn't look like the overlay did. Obviously — because it never was a colour. It was a calculation whose result depended entirely on what was underneath.
Alpha is where most colour bugs live, and the reason is that transparent colours don't have values. They have formulas.
The Blending Equation
Standard alpha compositing — the "source over" operation — combines a foreground colour with a background using a single equation applied per channel:
result = (foreground × alpha) + (background × (1 − alpha))
Work through the black-over-white example, channel by channel. Foreground R is 0, background R is 255, alpha is 0.5:
R = (0 × 0.5) + (255 × 0.5) = 127.5
Rounded, that's 128, or 0x80. Same for green and blue. Hence #808080.
Now put the same overlay on a mid-blue background, #3366CC (51, 102, 204):
R = (0 × 0.5) + (51 × 0.5) = 25.5 → 26
G = (0 × 0.5) + (102 × 0.5) = 51
B = (0 × 0.5) + (204 × 0.5) = 102
Result: #1A3366. Completely different colour, identical CSS declaration.
This is the whole insight. An rgba value is an instruction, not a colour. You can convert it to a hex value only if you know exactly what it's sitting on.
Converting Between the Two
There are two directions, and they have different difficulty.
rgba to hex, given a known background
Straightforward. Apply the equation with your specific background and convert the result. The Colour Converter will translate the resulting RGB triple into hex, HSL, HSV or CMYK:
- Compute the blended RGB values with the equation above.
- Enter the resulting RGB into the converter.
- Read the hex, HSL and other representations.
This is worth doing whenever you're locking down a design token. If a semi-transparent value is always used over one known surface, replacing it with the flattened opaque hex is faster to render and easier to reason about.
hex to rgba, choosing an alpha
Harder, and often impossible.
Say you want #5A7FBF to be expressed as some colour at 40% alpha over a white background. Solve the equation for the foreground:
foreground = (result − background × (1 − alpha)) / alpha
For the red channel: (90 − 255 × 0.6) / 0.4 = (90 − 153) / 0.4 = −157.5
Negative. Out of range. No colour at 40% alpha over white can produce that result — white is too light and 40% too weak to pull it down that far.
The general rule: compositing over a background can only produce colours between the foreground and background. A semi-transparent overlay over white can never produce something darker than the fully opaque foreground, and can never produce something lighter than white.
Raise the alpha and the range of achievable results widens. At alpha 1.0 you can hit anything; at 0.1 you can barely shift the background at all.
Where This Bites
Design handoff. A designer picks a colour from a rendered mockup and hands over the hex. That hex encodes the mockup's background. Used on a different surface, it looks wrong — and nobody can figure out why, because the value was copied correctly.
Dark mode. A semi-transparent border that reads as a subtle grey line on white becomes nearly invisible on a dark surface, because the blending pulls it toward the dark background instead. Overlays and shadows built with alpha need separate treatment per theme, or need to be redefined in terms of surface-relative tokens.
Stacked transparencies. Three overlapping 50%-alpha layers don't produce 150% of anything. Each composites onto the result of the one below:
Layer 1 over background: 50% of the way
Layer 2 over that: 75% of the way
Layer 3 over that: 87.5% of the way
The formula for n identical layers at alpha a is 1 − (1−a)ⁿ. It approaches full opacity asymptotically and never reaches it. This explains why stacking shadows never quite produces solid black.
Contrast checking. WCAG contrast ratios are computed between two opaque colours. A semi-transparent text colour has no fixed contrast ratio — it has one per background it appears over. Checking the unblended value gives a meaningless number. Always flatten against the actual background before measuring.
Anti-aliased edges. Every anti-aliased letterform edge is alpha compositing at work. This is why text rendered light-on-dark can appear heavier than the same text dark-on-light, and why some rendering engines apply gamma correction to compensate.
The opacity Property Is Different
Easy to conflate, genuinely different behaviour.
/* Applies alpha to one colour */
background-color: rgba(255, 0, 0, 0.5);
/* Applies alpha to the entire rendered element */
opacity: 0.5;
background-color: rgba(...) makes only the background semi-transparent. Text and borders inside remain fully opaque.
opacity creates a new stacking context, renders the whole element — text, borders, children, everything — into an offscreen buffer, and then composites that buffer at the given alpha. Children cannot exceed the parent's opacity, no matter what values they set.
The practical consequence: setting opacity: 0.5 on a card fades the text as well, usually harming readability. If you only wanted a translucent background, use an alpha colour value.
There's also a performance dimension. opacity on an animated element is typically compositor-accelerated, which makes it one of the cheapest properties to animate. Animating a background colour's alpha channel usually isn't.
Premultiplied Alpha
Worth knowing about if you work with images, canvas, or graphics APIs.
Straight (non-premultiplied) alpha stores colour and alpha independently: RGB values are the actual colour, A is the coverage.
Premultiplied alpha stores RGB values already multiplied by A. A 50%-alpha pure red is stored as (127.5, 0, 0, 0.5) rather than (255, 0, 0, 0.5).
Premultiplication makes the compositing equation cheaper — one multiply instead of two — and, more importantly, makes filtering correct. When a graphics system interpolates between a fully transparent pixel and an opaque red one, straight alpha interpolates the RGB values too, blending toward whatever arbitrary colour the transparent pixel happened to store. Transparent pixels are often stored as black, which produces dark fringes around the edges of scaled or rotated images.
If you've ever seen a mysterious dark halo around a transparent PNG after resizing, that's it.
Canvas getImageData returns non-premultiplied values in browsers, but the internal representation may be premultiplied, which introduces small rounding differences on round-trip. Don't rely on pixel-exact round-tripping through canvas.
Practical Tips
Flatten alpha into tokens where the background is fixed. If an overlay always sits on your one surface colour, store the resulting opaque hex. Fewer surprises, simpler contrast checking.
Keep alpha where the background varies. Modals, tooltips, hover states over unpredictable content — these genuinely need to blend.
Define theme-aware overlay tokens. Rather than a single --overlay: rgba(0,0,0,0.05), define a token that resolves differently in light and dark themes.
Always contrast-check the flattened result. Never the raw semi-transparent value.
Use rgba or the eight-digit hex form for colour alpha; reserve opacity for fading whole elements. The eight-digit form (#RRGGBBAA) is well supported and keeps everything in one notation.
FAQ
Can I convert rgba to hex? Only against a specific background. The hex value you get is the blended result for that background and won't reproduce the same appearance elsewhere.
What is #FF0000CC?
Eight-digit hex: pure red with an alpha of CC (204 out of 255, about 80%). The last two digits are the alpha channel.
Why do my overlapping semi-transparent shadows never look fully black? Each layer composites onto the previous result, covering a fraction of the remaining distance. Total opacity approaches 1 asymptotically without reaching it.
Does alpha affect WCAG contrast ratios? Yes, entirely — the ratio depends on the blended result. Flatten against the actual background before measuring.
What causes dark edges around resized transparent images? Almost always a premultiplied-versus-straight alpha mismatch during filtering.
The Takeaway
A transparent colour is a function waiting for an argument. Once you're thinking about alpha as a blending instruction rather than a value, dark-mode overlay bugs, mismatched design handoffs and impossible contrast checks all stop being mysterious.
Convert between HEX, RGB, HSL, HSV and CMYK free with the Colour Converter at sadiqbd.com — no sign-up, instant results.