Where a Background Is Actually Painted
background-color looks like the simplest property in CSS, and it mostly is, but knowing exactly how far it spreads explains a lot of small surprises. A background is painted behind the content and behind the padding, and it extends all the way underneath the border. It never reaches into the margin — that is why margin is described as transparent space.
This is why padding and margin produce different-looking results even when the number is the same. Add padding: 24px to a coloured card and the coloured area grows. Add margin: 24px and the card stays exactly the same size, but everything around it moves away. Once you have seen a background reveal that difference, the two properties stop being confusable.
The border detail matters when the border is not solid. A dashed or dotted border has gaps, and by default the background shows through them, which can look untidy. background-clip: padding-box stops the background at the inner edge of the border, so the gaps stay clear. Unlike color, background-color is not inherited — each element paints its own, and an element with no background of its own is simply see-through, letting its parent's show.
/* Background covers content + padding + under the border */
.card {
background-color: #ffffff;
padding: 24px; /* the white area grows */
border: 2px dashed #d1039e;
margin: 16px; /* stays transparent */
}
/* Stop the background before the border */
.card--clean {
background-clip: padding-box;
}
/* Not inherited: this heading has no background of its own */
.card h2 { color: #d1039e; } /* it shows the card's white through */ - A background set on
bodyis a special case: ifhtmlhas no background of its own, the browser propagates the body's background to fill the entire canvas, even the part of the window below where your content ends. That is why a short page with a coloured body still has colour all the way down.
Images: repeat, position, and the cover / contain Decision
background-image: url('hero.jpg') puts a picture behind an element. On its own that image tiles — it repeats across and down to fill the box, which is exactly right for a subtle texture and completely wrong for a photograph. Four companion properties turn that raw behaviour into something usable.
background-repeat switches tiling off with no-repeat, or restricts it to one direction with repeat-x and repeat-y. background-position says where the image sits inside the box; the default is the top-left corner, and center is what you usually want for a photo. You can also use two values, as in center top, or percentages for finer control.
background-size is the one worth slowing down for, because cover and contain solve opposite problems. cover scales the image until it fills the whole box, keeping its proportions, and crops whatever hangs over the edges — you never see a gap, but you may lose part of the picture. contain scales until the entire image fits inside the box, again keeping proportions, which means you see all of it but there will usually be empty space along one pair of edges. For a hero banner you want cover. For a logo that must not be cut, you want contain.
There is a fifth property, background-attachment: fixed, which holds the image still while the page scrolls past it. It creates a pleasant effect on desktop, but it behaves inconsistently on mobile browsers and can make scrolling stutter, so treat it as decoration you can afford to lose rather than something a design depends on.
/* A hero banner */
.hero {
background-image: url('/images/campus.jpg');
background-repeat: no-repeat;
background-position: center;
background-size: cover; /* fills the box, crops the excess */
min-height: 60vh;
}
/* A logo that must never be cropped */
.brand-tile {
background-image: url('/images/logo.svg');
background-repeat: no-repeat;
background-position: center;
background-size: contain; /* whole image visible, may leave gaps */
width: 160px;
height: 80px;
}
/* A repeating texture — tiling is the point here */
.paper { background-image: url('/images/grain.png'); }
/* The shorthand: colour, image, repeat, position / size */
.hero {
background: #2b2b2b url('/images/campus.jpg') no-repeat center / cover;
} background-repeat: no-repeat— stop the image tilingbackground-position: center— the sensible default for photographsbackground-size: cover— fill the box, crop the overflowbackground-size: contain— fit the whole image in, accept the gaps- In the shorthand, size comes after position separated by a slash:
center / cover - The shorthand resets every background property you leave out, including the image
Gradients Are Images, Not Colours
This catches almost everyone once: background-color: linear-gradient(...) does nothing. A gradient is not a colour value — it is a generated image, so it belongs in background-image or in the background shorthand. Write it in the colour slot and the declaration is invalid and silently discarded, which sends people hunting for a browser bug that is not there.
linear-gradient() takes a direction followed by two or more colours. The direction can be an angle (135deg) or a keyword phrase (to right, to bottom right). Colours are spread evenly by default, and you can pin any of them to a position by adding a percentage. radial-gradient() works the same way but spreads outward from a point instead of along a line.
One limitation to know before you plan an effect around it: gradients generally do not animate. Putting a transition on background-image and swapping one gradient for another usually gives you an abrupt jump rather than a smooth fade. The standard way around it is to keep one gradient and animate something else — a larger background-size plus a moving background-position, or a second layer whose opacity you change.
/* Wrong — a gradient is not a colour */
.hero { background-color: linear-gradient(#d1039e, #7a0060); } /* ignored */
/* Right */
.hero { background-image: linear-gradient(#d1039e, #7a0060); }
.hero { background: linear-gradient(135deg, #d1039e, #7a0060); }
/* Direction by keyword or by angle */
.a { background: linear-gradient(to right, #d1039e, #7a0060); }
.b { background: linear-gradient(45deg, #d1039e, #7a0060); }
/* Colour stops let you control where each colour lands */
.c { background: linear-gradient(to bottom, #ffffff 0%, #ffffff 60%, #f4f4f7 100%); }
/* A hard edge instead of a blend — two stops at the same position */
.stripe { background: linear-gradient(to right, #d1039e 50%, #7a0060 50%); }
/* Radial spreads from a point */
.glow { background: radial-gradient(circle at center, #d1039e, #5b026b); } - Two colour stops placed at the same percentage produce a sharp line rather than a blend. That is how people draw stripes, progress bars and split backgrounds without any image file at all.
Layering Backgrounds, and Text You Can Still Read
One element can have several backgrounds at once. Separate them with commas, and remember the stacking order: the first one listed sits on top, and the last one is furthest back. Only the final layer may have a background colour, because a colour always fills the whole box and anything behind it would be invisible anyway.
The most useful application of this is the problem every hero banner runs into. White text over a photograph is perfectly readable until the photo happens to be bright in that corner, and then it vanishes. Rather than editing the image, layer a dark semi-transparent gradient on top of it in CSS. The photo still shows through, the text sits on a consistently darker surface, and you can tune the strength by changing one number.
A last point that is about correctness rather than looks: a background image is decoration. It has no alt text, screen readers do not announce it, and search engines do not treat it as content. If the picture carries meaning — a product photo, a chart, a person the article is about — it belongs in an <img> tag with a proper alt description. Reserve CSS backgrounds for textures, banners and decorative flourishes, which is exactly what they are good at.
/* Layered: gradient on top, photo behind it, colour at the very back */
.hero {
background:
linear-gradient(rgba(0, 0, 0, 0.55), rgba(0, 0, 0, 0.25)),
url('/images/campus.jpg') center / cover no-repeat,
#2b2b2b;
color: #ffffff;
padding: 80px 24px;
}
/* Each layer can have its own position and size */
.pattern {
background:
url('/images/dots.png') repeat,
linear-gradient(#ffffff, #f4f4f7);
} - Layers are comma-separated; the first is on top
- Only the last layer may carry a background colour
- A dark gradient over a photo is the standard fix for unreadable hero text
- Background images are decorative — meaningful pictures need
<img>andalt - Large background photos are often the heaviest thing on a page; compress them and consider a smaller image for phones inside a media query
