CSS Background Image Opacity: How to Change It
Learn how to fade a CSS background image without fading text, add overlays, layer images, troubleshoot stacking, and automate captures.

Short answer: CSS has no property that changes only the alpha of a background-image. Applying opacity to the element fades everything inside it, including headings, links, and buttons. Use a translucent gradient when you want a tint or dark wash. Use a separate pseudo-element or child layer when the image itself must become transparent while the content stays fully opaque.
MDN describes the same distinction: to change the opacity of a background only, use a background value with an alpha channel. The practical implementation depends on whether you want to colorize the image or reveal the page behind it. This guide covers both patterns, layering rules, blending, accessibility, performance, debugging, and an API option for generating the resulting screenshots.
1. Pick the result you actually want
| Desired result | Use | What changes |
|---|---|---|
| Dark, light, or colored wash over a photo | Alpha gradient as the first background layer | A color is composited over the image |
| Image becomes see-through while text remains solid | Separate pseudo-element or child layer | Only the image layer receives opacity |
| Multiply, screen, or another artistic interaction | background-blend-mode |
Pixel blending between background layers |
| Fade the entire component | opacity on the container |
Container, descendants, and backgrounds |
An overlay does not alter the image file’s intrinsic alpha. It places another layer above the image. If you need the content behind the image to show through, use an independently positioned image layer.
2. Add a translucent overlay with a gradient
This is the smallest solution for a darkened hero image that keeps foreground content readable. The first comma-separated background layer is painted closest to the user, so put the gradient before the URL.

.hero {
min-height: 24rem;
color: white;
background-image:
linear-gradient(rgb(0 0 0 / 0.35), rgb(0 0 0 / 0.35)),
url('hero.jpg');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
The two gradient stops use the same color, producing a uniform 35% black wash. Change 0.35 to any value from 0 (transparent) to 1 (opaque). A colored overlay works the same way:
.product-card {
background-image:
linear-gradient(rgb(20 80 160 / 0.45), rgb(20 80 160 / 0.45)),
url('product.jpg');
background-size: cover;
background-position: center;
}
You can vary the gradient to improve contrast only where text sits:
.hero {
background-image:
linear-gradient(90deg,
rgb(0 0 0 / 0.75) 0%,
rgb(0 0 0 / 0.35) 52%,
rgb(0 0 0 / 0.05) 100%),
url('hero.jpg');
}
Why background-color alone often appears not to work
.hero {
background-color: rgb(0 0 0 / 0.35);
background-image: url('hero.jpg');
}
Background color is painted beneath background image layers. An opaque image therefore hides the color. The gradient technique creates a layer above the image and is reliable for an overlay. See MDN’s background-image layering documentation.
3. Make only the background image translucent
When the requirement is “transparent background image, opaque text,” create a separate layer. A pseudo-element keeps the markup small:

.hero {
position: relative;
isolation: isolate;
min-height: 24rem;
color: white;
}
.hero::before {
content: '';
position: absolute;
inset: 0;
z-index: -1;
background: url('hero.jpg') center / cover no-repeat;
opacity: 0.35;
pointer-events: none;
}
.hero-content {
position: relative;
z-index: 0;
}
Here, opacity applies to ::before, so the heading and button are unaffected. position: relative establishes the containing block; isolation: isolate makes the stacking context predictable when the surrounding page has blend modes or positioned elements.
A real child is easier to inspect when you need more complex interactions:
<section class='hero'>
<div class='hero-image' aria-hidden='true'></div>
<div class='hero-content'>
<h1>Ship faster</h1>
<a class='button' href='/start'>Start now</a>
</div>
</section>
.hero {
position: relative;
min-height: 24rem;
overflow: hidden;
}
.hero-image {
position: absolute;
inset: 0;
background: url('hero.jpg') center / cover no-repeat;
opacity: 0.35;
}
.hero-content {
position: relative;
z-index: 1;
}
Use pointer-events: none on a decorative image layer if it could intercept clicks. If you need a foreground overlay plus an image, use explicit z-index values and keep both layers inside the same positioned container.
4. Understand CSS background and opacity rules
Opacity belongs to an element
.hero {
opacity: 0.35;
}
This fades the complete rendered element, including descendants. It is correct for fading a whole card, but it cannot selectively spare text. MDN’s opacity reference documents the 0-to-1 range and the element-wide behavior.
Layer order is first to last
For background-image: A, B, A is the top layer and B is below it. This is why the translucent gradient must come first. Each layer can have its own position, size, and repeat value:
.hero {
background-image: linear-gradient(rgb(0 0 0 / .3), rgb(0 0 0 / .3)), url('hero.jpg');
background-position: center, center;
background-size: cover, cover;
background-repeat: no-repeat, no-repeat;
}
Blend modes are not opacity controls
background-blend-mode changes how corresponding background layers combine. It is useful for effects such as multiply or screen, but it does not provide a simple “image at 40%” control. The modes follow the background-image list. mix-blend-mode instead blends an element’s content with the backdrop in its stacking context. Consult MDN’s background-blend-mode and mix-blend-mode references before using them for an intentional visual effect.
5. Responsive and visual edge cases
- Text contrast: Test the darkest and lightest parts of the image at every breakpoint. Increase overlay alpha or add a directional gradient where necessary.
- Variable image height: An absolutely positioned layer follows the container. Give the container a defined height, minimum height, or aspect ratio.
- Rounded corners: Put
overflow: hiddenon the positioned container so the image layer follows the radius. - Transformed ancestors: A transform can create a new containing block and stacking context. Inspect ancestors if z-index behaves unexpectedly.
- Animations: Animate the pseudo-element’s opacity rather than the parent. This avoids fading controls during a hover transition.
- Reduced motion: If opacity is animated, provide a
prefers-reduced-motionoverride. - Transparent PNG/WebP: An image with its own alpha can be combined with a gradient; the final result is the composition of both alpha values.
- Image meaning: Background images are not announced by screen readers. If the image communicates information, provide equivalent semantic text or an actual informative
<img>with alt text. MDN’s accessibility guidance covers this distinction.
6. Troubleshooting checklist
| Symptom | Likely cause | Fix |
|---|---|---|
| Text fades with the image | opacity is on the parent |
Move the image to ::before or a child layer. |
| Overlay color is invisible | Color is beneath an opaque image | Use a gradient as the first background layer. |
| Pseudo-element covers the text | Stacking order or z-index is wrong | Add position: relative, isolation: isolate, and explicit z-index values. |
| Button cannot be clicked | Decorative layer intercepts pointer input | Set pointer-events: none on that layer. |
| Image spills outside rounded corners | Container does not clip children | Set overflow: hidden on the container. |
| Image appears stretched | Missing or mismatched background sizing | Use background-size: cover or define a deliberate aspect ratio. |
| Blend result changes elsewhere on the page | Unexpected stacking context | Use isolation: isolate and remove unnecessary transforms or blend modes. |
7. Performance, reliability, and maintainability
CSS overlays add negligible layout work compared with downloading and decoding the image itself. The largest practical costs are image bytes, decoding, and repeated animated repaints. Serve appropriately sized responsive assets, prefer modern formats where your delivery pipeline supports them, and avoid animating a large full-screen layer continuously.
Keep the image layer decorative when possible. That lets you mark it aria-hidden in markup and prevents it from competing with meaningful content. Centralize the alpha in a custom property when a design system needs consistent tuning:
.hero {
--hero-image-alpha: .35;
position: relative;
isolation: isolate;
}
.hero::before {
content: '';
position: absolute;
inset: 0;
background: url('hero.jpg') center / cover no-repeat;
opacity: var(--hero-image-alpha);
}
For screenshot regression tests, capture representative light and dark images, narrow and wide viewports, and states with long headings. A contrast adjustment that works for one photograph can fail for another.
8. Or skip the browser setup
If your goal is to generate screenshots of a page that uses these CSS techniques, ScreenshotNeo provides a single HTTP request that returns PNG, JPEG, WebP, or PDF. The API loads the page in a browser, so gradients, pseudo-elements, custom fonts, and responsive backgrounds are rendered together. See the ScreenshotNeo API documentation for all options.
cURL
curl -G 'https://api.screenshotneo.com/v1/shot' \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-o shot.webp
Python
import requests
r = requests.get(
'https://api.screenshotneo.com/v1/shot',
params={'access_key': 'YOUR_API_KEY', 'url': 'https://stripe.com'},
timeout=90,
)
r.raise_for_status()
open('shot.webp', 'wb').write(r.content)
Node.js
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://stripe.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, timeouts, failed loads, and cache hits are not billed. Every response identifies the result with X-Page-Verdict and X-Billed headers. You can set full-page capture, a CSS element selector, dark mode, a device preset or custom viewport, retina scale, custom CSS and JavaScript, click and wait behavior, blocked resources, headers, cookies, user agent, authorization, timezone, geolocation, transparent backgrounds, resizing, a chosen cache TTL, signed links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, and PDF paper, margin, landscape, and page-range options. An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.
The Free plan includes 1,000 shots each month with no card. Paid plans start at $5 for 3,000 shots; every feature is included on every plan. Create a free ScreenshotNeo account and try the capture before adding billing.
9. FAQ
Can I use background-color: transparent to fade only the image?
No. Transparency in the color affects the color layer. An opaque image above it still hides the page. Use a gradient overlay or an independent image layer.
Should I use JavaScript?
No for the basic effect. CSS layering handles tinting and independent image opacity. JavaScript is only useful when opacity changes in response to application state or interaction.
Does filter: opacity() solve this?
Applied to the container, it has the same broad problem as opacity: the rendered contents are affected. Apply it only to a dedicated image layer if you choose that approach.
How do I fade an image on hover without fading the link?
Put the image in a pseudo-element or sibling and transition that layer’s opacity. Keep the link in a separate foreground layer.
Which technique is best for readable text?
Start with a gradient overlay, because it lets you darken only the side behind the text. If the page behind the image must show through, use the pseudo-element pattern and verify contrast at each viewport.


