What CSS opacity is
The opacity property controls how transparent an element is.
It affects the entire element: its background, its text, its children, its borders, everything.
Think of it like turning down the “visibility” knob for the whole layer.
If you only want the background to be transparent (but keep the text crisp and fully opaque),
don’t reach for opacity first — you’ll usually want an alpha color (rgba(),
hsl() with alpha, or hex with alpha),
or a pseudo-element trick. We’ll do both in this tutorial.
CSS opacity property and values
opacity accepts a number from 0 to 1:
- 1 means fully opaque (default).
- 0 means fully transparent (it still takes up space unless you also change layout).
- Values like 0.5 are 50% opacity.
Important beginner gotcha: opacity applies to the element and all its descendants as a single “group”.
If you set a parent to opacity: 0.5;, the children are also faded. There is no “un-fade the child”
spell.
Interactive: opacity values (0 to 1)
Drag the slider to set the opacity of the card.
.card {
opacity: 0.85;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
}
.card {
border: 3px solid #111;
border-radius: 16px;
padding: 16px;
background: #fff;
display: grid;
gap: 10px;
box-shadow: 0 12px 0 #111;
}
.card h4 {
margin: 0;
font-size: 18px;
}
.card p {
margin: 0;
line-height: 1.5;
}
.pills {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.pill {
padding: 6px 10px;
border: 2px solid #111;
border-radius: 999px;
background: #f2f2f2;
font-size: 13px;
}
Opacity demo card
Opacity fades everything inside this card: background, text, pills, borders.
Child Elements Fade too
Opacity gotchas: clickability, layering, and “invisible but still there”
-
Opacity doesn’t remove layout. An element with
opacity: 0;is invisible, but it still takes up space. -
Opacity can still capture clicks. An invisible element can block interactions underneath. If
you’re fading something out for UI states,
you often pair opacity with
pointer-events: none;. -
Opacity creates a stacking context. This matters when you’re using
z-indexwith overlapping elements.
Example: invisible but blocking clicks
In the first snippet, the faded overlay still blocks clicks. In the second snippet, we add
pointer-events: none;
so clicks go through it.
.overlay {
opacity: 0.35;
}
.overlay {
opacity: 0.35;
pointer-events: none;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
display: grid;
gap: 14px;
}
.stage {
position: relative;
border: 3px solid #111;
border-radius: 16px;
overflow: hidden;
padding: 16px;
background: #fff;
min-height: 160px;
display: grid;
gap: 12px;
}
.actions {
display: flex;
flex-wrap: wrap;
gap: 10px;
align-items: center;
}
.button {
display: inline-block;
padding: 10px 12px;
border: 3px solid #111;
border-radius: 12px;
background: #f2f2f2;
font-weight: 700;
font-size: 14px;
}
.note {
margin: 0;
opacity: 0.8;
line-height: 1.5;
}
.overlay {
position: absolute;
inset: 0;
background: linear-gradient(120deg, #0077b6 0%, #90e0ef 55%, #caf0f8 100%);
}
Try clicking meIf the overlay blocks clicks, the link feels “dead”. Add
pointer-events: none;to fix it.
CSS opacity transition
Transitions are great when you want a gentle fade between two states (like hover, focus, or a class toggle).
The most common pattern is: start at opacity: 1, fade to something lower on hover, and add a
transition so it doesn’t snap.
Pro tip: if you ever animate opacity for UI, consider prefers-reduced-motion so users who prefer less
motion
don’t get forced fades everywhere.
Opacity transition on hover
.card:hover {
opacity: 0.55;
}
.card:hover {
opacity: 0.55;
}
.card {
transition: opacity 220ms ease;
}
.card:hover {
opacity: 0.55;
}
.card {
transition: opacity 220ms ease;
}
@media (prefers-reduced-motion: reduce) {
.card {
transition: none;
}
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
}
.card {
border: 3px solid #111;
border-radius: 16px;
padding: 16px;
background: #fff;
display: grid;
gap: 10px;
box-shadow: 0 12px 0 #111;
}
.card h4 {
margin: 0;
font-size: 18px;
}
.card p {
margin: 0;
line-height: 1.5;
opacity: 0.85;
}
.hint {
margin: 0;
font-size: 13px;
opacity: 0.8;
}
Hover me (or focus me)
Without a transition, opacity changes instantly. With a transition, it fades smoothly.
Try hovering the card with your mouse.
CSS opacity animation
Animations are best when you want opacity to keep changing over time (like pulsing, blinking, loading indicators, or
attention cues).
Most opacity animations use @keyframes.
- Animate opacity for small emphasis (avoid rapid blinking, it’s annoying and can be unsafe).
-
Prefer small ranges (like
1down to0.6) for “pulse” effects. -
Combine with
prefers-reduced-motionfor accessibility.
Opacity pulse animation
@keyframes pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.55;
}
}
.badge {
animation: pulse 1.1s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
.badge {
animation: none;
}
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
display: grid;
gap: 14px;
}
.row {
display: flex;
flex-wrap: wrap;
gap: 12px;
align-items: center;
}
.card {
border: 3px solid #111;
border-radius: 16px;
padding: 16px;
background: #fff;
box-shadow: 0 12px 0 #111;
display: grid;
gap: 10px;
min-width: 280px;
}
.card p {
margin: 0;
opacity: 0.85;
line-height: 1.5;
}
.badge {
display: inline-block;
padding: 8px 12px;
border: 3px solid #111;
border-radius: 999px;
background: #f2f2f2;
font-weight: 800;
font-size: 13px;
width: max-content;
}
NewThis badge pulses by animating
opacity.LiveKeep it subtle: small fades feel polished, big fades feel like a disco sign.
CSS Opacity Background
This is where beginners get tricked most often:
opacity affects the entire element.
So if you set opacity on a card just to fade its background, you will also fade the text.
Let’s handle background opacity the right way, depending on what kind of “background” you might want.
CSS Opacity Background Color
If you only want a semi-transparent background color, use an alpha color instead of opacity.
That keeps the text fully opaque.
-
rgba(0, 0, 0, 0.25)is classic. -
hsl(210 90% 40% / 0.25)is modern and very readable. -
Hex alpha like
#00000040is also possible (the last two digits are the alpha).
.card {
background: rgba(0, 119, 182, 0.18);
}
.card {
background: hsl(200 90% 45% / 0.18);
}
.card {
background: #0077b62e;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
}
.card {
border: 3px solid #111;
border-radius: 16px;
padding: 16px;
display: grid;
gap: 10px;
box-shadow: 0 12px 0 #111;
}
.card h4 {
margin: 0;
font-size: 18px;
}
.card p {
margin: 0;
line-height: 1.5;
opacity: 0.9;
}
Transparent background, solid text
The background uses an alpha color. Notice the text stays crisp and fully opaque.
CSS Opacity of Background Image
You can’t set opacity on just the background image directly.
If you do opacity: 0.4; on the element, you fade the text too.
The common solution is a pseudo-element (::before) that holds the background image.
Then you set opacity on the pseudo-element, not on the content.
.hero {
position: relative;
overflow: hidden;
}
.hero::before {
opacity: 0.35;
}
.hero::before {
content: "";
position: absolute;
inset: 0;
background: url("https://picsum.photos/1200/700") center / cover no-repeat;
}
.hero > * {
position: relative;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
}
.hero {
border: 3px solid #111;
border-radius: 18px;
padding: 22px;
background: #fff;
box-shadow: 0 12px 0 #111;
display: grid;
gap: 10px;
min-height: 240px;
align-content: end;
}
.hero h4 {
margin: 0;
font-size: 20px;
}
.hero p {
margin: 0;
max-width: 55ch;
line-height: 1.55;
opacity: 0.9;
}
.tag {
width: max-content;
padding: 7px 10px;
border: 3px solid #111;
border-radius: 999px;
background: #f2f2f2;
font-weight: 800;
font-size: 12px;
}
Pseudo-element backgroundBackground image fades, content stays solid
The image lives on
::before, so we can fade it independently withopacity.
Learn more about ::before in the CSS ::before and ::after Pseudo-Elements Interactive Tutorial.
CSS opacity gradient
Sometimes you don’t want a uniform transparency. You want a gradual fade, like an image that fades out to transparent.
That’s where CSS masking shines. A mask controls which parts of an element are visible. White areas in the mask show the element, black areas hide it, and gray gives partial transparency.
In practice, we often use mask-image: linear-gradient(...) (and the -webkit- prefixed
version)
to create a soft “opacity gradient” without editing the image.
Mask an image with a gradient fade
.media img {
-webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 65%, transparent 100%);
mask-image: linear-gradient(to bottom, #000 0%, #000 65%, transparent 100%);
}
.media img {
-webkit-mask-image: linear-gradient(to right, #000 0%, #000 60%, transparent 100%);
mask-image: linear-gradient(to right, #000 0%, #000 60%, transparent 100%);
}
.media img {
-webkit-mask-image: radial-gradient(circle at 40% 30%, #000 0%, #000 55%, transparent 72%);
mask-image: radial-gradient(circle at 40% 30%, #000 0%, #000 55%, transparent 72%);
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
display: grid;
gap: 14px;
}
.card {
border: 3px solid #111;
border-radius: 18px;
overflow: hidden;
background: #fff;
box-shadow: 0 12px 0 #111;
}
.media {
aspect-ratio: 16 / 9;
background: #f2f2f2;
}
.media img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.caption {
padding: 14px 16px 16px 16px;
display: grid;
gap: 8px;
}
.caption h4 {
margin: 0;
font-size: 18px;
}
.caption p {
margin: 0;
opacity: 0.85;
line-height: 1.55;
max-width: 65ch;
}
![]()
Mask gradient fade
The image is faded using a mask gradient. The element is still “fully opaque” in CSS terms, but the mask makes part of it transparent.
CSS opacity fade
A super practical use-case: fading out the bottom of a text block so it hints there’s more content. This is common in cards, previews, and “read more” sections.
Again, we’ll use a mask. The text remains selectable and accessible (it isn’t replaced by an overlay), but it becomes gradually transparent near the bottom.
Fade out the bottom of content with a mask
.preview {
-webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 70%, transparent 100%);
mask-image: linear-gradient(to bottom, #000 0%, #000 70%, transparent 100%);
}
.preview {
-webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 55%, transparent 92%);
mask-image: linear-gradient(to bottom, #000 0%, #000 55%, transparent 92%);
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
max-width: 980px;
padding: 18px;
font-family: system-ui, Arial, sans-serif;
display: grid;
gap: 14px;
}
.card {
border: 3px solid #111;
border-radius: 18px;
background: #fff;
box-shadow: 0 12px 0 #111;
padding: 16px;
display: grid;
gap: 10px;
}
.card h4 {
margin: 0;
font-size: 18px;
}
.preview {
border: 2px solid #111;
border-radius: 14px;
padding: 14px;
background: #f2f2f2;
max-height: 140px;
overflow: hidden;
}
.preview p {
margin: 0;
line-height: 1.6;
opacity: 0.9;
}
.meta {
margin: 0;
font-size: 13px;
opacity: 0.8;
}
Bottom fade preview
Opacity fades are useful when you want to hint that content continues without showing a hard cutoff. This technique is commonly used for text previews, descriptions in cards, and “read more” UI.
The mask doesn’t change layout, it just changes visibility. And because this is still real text, it stays selectable and accessible.
Learn more about CSS masks in the CSS Mask Interactive Tutorial.
Opacity vs other techniques
-
Use
opacitywhen you truly want to fade the entire element (and its children) as a whole. -
Use alpha colors (
rgba(),hsl(... / ...), hex alpha) when you only want a transparent background color. - Use pseudo-elements when you want to fade a background image without fading content.
- Use CSS masks when you want a gradient fade or a partial fade in a specific area.
Quick opacity checklist
-
Need to fade the whole component? Use
opacity. - Need to fade only the background color? Use an alpha color.
-
Need to fade only the background image? Put it on
::beforeand set opacity there. -
Need a gradient fade? Use
mask-image(and include-webkit-mask-imagefor best coverage). -
Fading UI in/out? Consider
pointer-eventsandprefers-reduced-motion.
That’s opacity for you: a simple property with a few sneaky side effects. Once you know when to switch to alpha colors, pseudo-elements, or masks, you’ll stop fighting it and start using it like a pro.
