Making a button semi-transparent once it is disabled, fading an element away smoothly, layering a dark veil over a background image: opacity is the fastest property for adding transparency to a page, provided its actual effect is properly understood.
Many beginners confuse it with a simple transparent colour, when its effect actually goes much further: it does not just tint, it progressively erases everything inside the element.
Definition
opacity sets the transparency of an entire element, using a number between 0 (fully invisible) and 1 (fully opaque), or an equivalent percentage. Unlike a background colour made transparent, opacity applies to the whole box: its background, its border, its text, and all of its children, which all inherit the same level of transparency with no distinction.
opacity versus a transparent colour (rgba, hsla)
opacity | rgba() and hsla() | |
|---|---|---|
| What becomes transparent | The whole element and its content | Only the targeted colour |
| Can target just the background, leaving text alone | No | Yes |
This difference explains why a card with a background of rgba(0, 0, 0, 0.5) keeps perfectly sharp, readable text, while the same card set to opacity: 0.5 sees its text turn grey and blurry, exactly like the rest of its content.
Concrete example: why the text becomes unreadable
.disabled-card {
opacity: 0.4;
}On a card containing a title, an image, and a button, this single line makes the whole thing transparent at once, text and image included. That is perfect for visually signalling a disabled state, but disastrous if the goal was only to darken the background: in that case, a colour set with rgba() applied to the background alone is the right tool.
The trap: opacity creates a stacking context
Any opacity value below 1 creates a new stacking context for the element in question, even with no z-index or position declared at all. A child of that element can then never appear in front of outside content, no matter its own z-index value, because the comparison stays locked inside that new group.
Common use cases
Beyond fade-in effects, opacity serves several very concrete situations on an interface. A disabled button often drops to opacity: 0.5 to visually signal that it no longer responds, in addition to a real disabled attribute on the HTML side. A product image darkens slightly to opacity: 0.85 on hover, giving visual feedback without changing its size or position. A header locked in place while scrolling can also use reduced opacity to let content scrolling just underneath show through, a light layering effect that costs very little in performance.
Frequently asked questions
Does opacity: 0 have the same effect as display: none?
Visually only. The element becomes invisible in both cases, but with opacity: 0 it keeps its spot in the layout, stays reachable by keyboard, and still reacts to clicks and hover, unlike display: none, which removes it from the page entirely.
Can opacity be animated?
Yes, and it is one of the few properties whose animation stays smooth on almost every device, even modest ones, because it does not force the browser to recompute layout on every frame. That is exactly why it is favoured for fades, far more than directly animating a transparent colour.
Why does my element's background seem to change colour with opacity?
Because opacity lets whatever sits behind the element show through, including the page's own background. A white card set to opacity: 0.8 placed over a coloured background takes on a tint slightly different from its original colour, an effect that never appears with a background colour made transparent through rgba() over an identical solid background. It is a good debugging reflex: if a colour looks oddly tinted while scrolling, a misplaced opacity is almost always the cause.