Common CSS Background Mistakes and How to Fix Them

Last reviewed on 2026-04-30

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

Symptom: A solid color shows where the image should be, or nothing shows. DevTools confirms the image URL but the request never fires, or the request 404s.

Three causes account for almost every instance:

2. background-size: cover crops the wrong part of the image

Symptom: The face, logo, or focal point gets cropped off-screen on certain viewport sizes — usually portrait phones or wide desktops.

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)

Symptom: A two-stop gradient that looked smooth in Figma renders with visible bands of color in the browser, especially on darker gradients on cheaper monitors.

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:

4. background-attachment: fixed is broken on iOS

Symptom: A fixed-attachment background works on desktop and on Android but on iOS Safari it scrolls with the page or stretches awkwardly.

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

Symptom: A gradient overlay that should sit on top of a photo is hidden behind it.

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

Symptom: A blend mode is set but the layers visibly stack instead of blending.

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

Symptom: Setting a new 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

Symptom: 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

Symptom: A sticky or fixed header looks correct at the top of the page, but as you scroll, the header's background disappears or the text under it bleeds through.

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:

10. The page background is wrong on short pages

Symptom: 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

Symptom: A CSS transition on 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

  1. Does the element have a non-zero height?
  2. Is the URL right relative to the file the rule is in?
  3. Is a child element painting over the background?
  4. Is a later background shorthand wiping earlier longhand?
  5. Is background-clip: text missing color: transparent?
  6. Is the issue iOS-specific? background-attachment: fixed is the usual culprit.
  7. Are gradient layers in the right order (first = on top)?
  8. Are you trying to blend across two elements? You want mix-blend-mode, not background-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.