Common CSS Background Mistakes and How to Fix Them
This is a debug page. Each section starts with a symptom — what the page looks like when it's wrong — followed by the most common cause and the fix. The list is ordered roughly by how often each one shows up in production code.
1. The background image isn't appearing at all
Three causes account for almost every instance:
- The element has no height. Block elements don't get an automatic height from their background. If the element is empty and you didn't set
min-heightorheight, the box is zero pixels tall and the image is technically painted into a zero-area region. Settingmin-height: 100vhon a hero is the usual fix. - The path is relative to the wrong file. A URL inside
style.cssresolves relative tostyle.css's location, not the HTML page's. If the stylesheet is at/css/site.cssand you wroteurl('hero.jpg'), the browser asks for/css/hero.jpg. Use a path that's correct relative to the CSS file or switch to a root-relative path:url('/images/hero.jpg'). - The element is opaque on top of the background. A child with a solid
background-colorcan completely cover the parent's background. Inspect the element and look at its computed paint stack.
2. background-size: cover crops the wrong part of the image
cover scales the image to fully cover the box; background-position decides which part stays visible when overflow has to be cropped. The default is 50% 50% (center), which is rarely what you want for a portrait subject.
Fix: position the focal point explicitly. If the subject is in the top third of the photo, write background-position: 50% 25%;. For art-directed responsive heroes, use a media query to shift position at mobile breakpoints, or switch to an <img> tag with object-position for finer control. The full reference is in the background-size: cover guide.
3. Gradient banding (visible stripes in a smooth gradient)
The cause is 8-bit color depth: when only 256 distinct values exist between two colors, gradients of similar lightness must repeat values, producing visible steps. Three fixes that compound:
- Add intermediate stops. A two-stop gradient often becomes invisible-stepped with a single mid stop.
linear-gradient(180deg, #000, #1a1a1a 50%, #333)bands less thanlinear-gradient(180deg, #000, #333). - Pick stops with bigger lightness range. Banding happens when adjacent values are so close that 8-bit can't represent the slope smoothly. Wider gradients band less.
- Layer a tiny noise texture. A semi-transparent SVG noise overlay at
opacity: 0.04dithers the gradient and the bands disappear. The pattern is covered in the CSS background patterns guide.
4. background-attachment: fixed is broken on iOS
iOS Safari has not implemented background-attachment: fixed the same way as desktop browsers since the introduction of the unified address bar. The official fix is not to use it on mobile.
The robust replacement is a fixed-position layer underneath the content:
.hero-bg {
position: fixed;
inset: 0;
background: url('hero.webp') center/cover no-repeat;
z-index: -1;
}
.content { position: relative; }
This achieves the same parallax-like effect on every browser, including iOS, and lets you animate or transform the layer independently. The full background-attachment: fixed page covers the older syntax for completeness but recommends this pattern for new work.
5. Multiple backgrounds in the wrong order
In a multi-value background declaration, the first listed layer is on top, not the bottom. Designers used to Photoshop's layer panel reach for the inverse order. The correct pattern for "darken a photo with a gradient":
.hero {
background:
/* this is on TOP — the dark gradient */
linear-gradient(rgba(0,0,0,0.55), rgba(0,0,0,0.85)),
/* this is on BOTTOM — the photo */
url('hero.webp') center/cover no-repeat;
}
The full ordering rules and worked examples are in multiple backgrounds.
6. background-blend-mode doesn't blend
Blend modes apply between background layers on the same element. They do not blend an element's background with whatever is rendered behind that element — that's mix-blend-mode, a separate property. If you wrote background-blend-mode hoping a child element would blend with its parent's background, switch to mix-blend-mode on the child. The blend modes page covers the distinction with examples.
7. background-image isn't shorthand-friendly
background-image on an element wipes out a previously set background-color.background (the shorthand) resets every sub-property it doesn't explicitly set. background-image by itself does not — it only sets one sub-property. The trap is using the shorthand later in the cascade and losing earlier overrides:
.card { background-color: #1a1a1a; padding: 2rem; }
/* This wipes out the color set above */
.card.featured { background: url('star.svg') no-repeat top right; }
Fix: either include the color in the shorthand (background: #1a1a1a url('star.svg') no-repeat top right;) or use the longhand background-image instead of the shorthand.
8. background-clip: text not working
background-clip: text on an element produces no visible text or no clipped fill.Two requirements: (1) you also need color: transparent (or -webkit-text-fill-color: transparent) so the text itself doesn't paint over the clipped background, and (2) the -webkit-background-clip: text prefix is still required for current Safari. The minimal pattern:
.gradient-text {
background: linear-gradient(90deg, #ff7a00, #ff3d8a);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
If you see the gradient as a rectangular block instead of as the text fill, the color: transparent step is missing. The full background-clip reference walks through the other valid values.
9. The fixed-position element loses its background on scroll
Most often this is a backdrop-filter or transparency issue, not a positioning bug. A header set to background: rgba(0,0,0,0.5) shows the page through it; if the page underneath is the same dark color, the header looks invisible. Two fixes:
- Raise the alpha to ~0.85 if the header is supposed to be readable on its own.
- Add
backdrop-filter: blur(12px)for the modern translucent-but-readable look. Pair withcolor-scheme: darkand a fallback solid color for browsers that don't support backdrop filter.
10. The page background is wrong on short pages
body { background: #000; } is set, but on short pages the area below the content shows the browser's default white instead.This is the html-vs-body trap. When body has a non-default background and the page content is shorter than the viewport, browsers may paint the area below using the html element's background, which is still default. Move the page background to html, or set both:
html, body { background: #000; min-height: 100%; }
11. Animating background-image doesn't tween
background-image snaps from one gradient to the other instead of fading smoothly.Browsers do not interpolate background-image values. The fix is structural: stack the two gradients on different layers, set one to opacity: 0, and animate the opacity:
.hero {
position: relative;
background: linear-gradient(135deg, #000, #1a1a1a);
}
.hero::after {
content: '';
position: absolute; inset: 0;
background: linear-gradient(135deg, #0a1530, #000);
opacity: 0;
transition: opacity .4s ease;
}
.hero:hover::after { opacity: 1; }
Quick Checklist When a Background Doesn't Work
- Does the element have a non-zero height?
- Is the URL right relative to the file the rule is in?
- Is a child element painting over the background?
- Is a later
backgroundshorthand wiping earlier longhand? - Is
background-clip: textmissingcolor: transparent? - Is the issue iOS-specific?
background-attachment: fixedis the usual culprit. - Are gradient layers in the right order (first = on top)?
- Are you trying to blend across two elements? You want
mix-blend-mode, notbackground-blend-mode.
For deeper coverage of the individual properties this page references, see the all properties reference, the cheat sheet, the multiple backgrounds guide, the blend modes page, the attachment-fixed page, and the size: cover page. For accessibility implications when fixing dark backgrounds, see accessible dark backgrounds. For performance considerations behind some of these fixes, see CSS background performance.