CSS background-position and background-size

When you attach a background image to an element with background-image, the browser has to answer two questions: where inside the element should the image sit, and how big should it be drawn? Those two questions are answered by background-position and background-size. Together they let you build hero banners, icon sprites, responsive logos, and textured panels that look correct no matter how the element is resized.

Both properties only matter once an element has a background image (or gradient) applied — they have no visible effect on solid background-color. Understanding exactly how the browser computes percentages and the cover/contain algorithms will save you from a lot of guesswork.

Overview / How it works

Every element with a background image has a conceptual rectangle called the background positioning area, which by default is the padding box of the element (the content box plus padding, but not the border). background-position moves the image’s origin point within that area, and background-size determines the rendered width and height of the image before it is positioned or repeated.

The rendering engine processes backgrounds in a specific order: first it resolves the image’s intrinsic size (its natural width/height, or aspect ratio for SVG/gradients), then it applies background-size to compute the actual rendered dimensions, then it applies background-position to place that sized image inside the positioning area, and finally background-repeat decides whether to tile copies of it. Because size is resolved before position, a percentage in background-position is always measured against the space that remains after sizing — not against the raw element dimensions.

By default, without either property set, a background image renders at its natural size and is placed at the top-left corner (0% 0%, equivalent to left top), then tiled by background-repeat: repeat.

Syntax

selector {
  background-position: x-position y-position;
  background-size: width height;
}
Property Accepted values Notes
background-position keywords (left, right, top, bottom, center), percentages, lengths, or a mix One value sets X (Y defaults to center); two values set X then Y; four values allow offsets like right 20px bottom 10px
background-size auto, a length, a percentage, cover, or contain One value sets width (height becomes auto, preserving aspect ratio); two values set width then height

Keyword reference

Value Meaning
cover Scale the image, preserving aspect ratio, so it completely fills the positioning area — some of the image may be cropped
contain Scale the image, preserving aspect ratio, so the whole image fits inside the area — this can leave empty space (letterboxing)
auto Use the image’s natural size for that axis, or derive it from aspect ratio if only one axis has an explicit length

Both properties can also be written as a single shorthand inside the background property, separated by a forward slash: background: url("photo.jpg") center / cover no-repeat;. Note the slash is mandatory when both position and size appear together — background-size can never appear on its own inside the shorthand without a preceding position and a slash.

Examples

Example 1: Basic position and size on a fixed box

.avatar {
  width: 160px;
  height: 160px;
  border: 2px solid #333;
  background-image: url("team-photo.jpg");
  background-repeat: no-repeat;
  background-position: center top;
  background-size: 200px auto;
}

Result: The 160x160px box shows the image scaled so its width is exactly 200px (taller than the box), with the height computed automatically to preserve the photo’s aspect ratio. The image is centered horizontally and aligned to the top of the box, so the bottom of the photo is cropped off by the box’s edges since it overflows.

This shows that background-size and the element’s own dimensions are independent — the image can be larger than its container, and only the visible portion inside the positioning area is shown (anything else is simply clipped by the box, not squeezed to fit).

Example 2: Precise offset positioning with four-value syntax

.badge {
  width: 300px;
  height: 150px;
  background-color: #f5f5f5;
  background-image: url("icon.svg");
  background-repeat: no-repeat;
  background-position: right 12px bottom 12px;
  background-size: 32px 32px;
}

Result: A light gray 300x150px box appears with a small 32x32px icon pinned 12px away from the right edge and 12px away from the bottom edge, regardless of the box’s overall size.

The four-value form (keyword, offset, keyword, offset) is the only way to offset a background image from an edge other than top-left using a positive length — without it, a plain length like 288px 106px would measure from the top-left corner instead, forcing you to recalculate the offset whenever the box resizes.

Example 3: Realistic responsive hero banner (shorthand)

.hero {
  min-height: 60vh;
  background: linear-gradient(rgba(0,0,0,0.35), rgba(0,0,0,0.35)),
              url("mountains.jpg") center 30% / cover no-repeat;
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
}

Result: A full-width section at least 60% of the viewport tall is filled edge-to-edge by the mountain photo, darkened by a semi-transparent black overlay gradient layered on top of it, with the image cropped so the interesting part around 30% down from the top stays visible (rather than dead-center) as the browser window is resized.

This combines two backgrounds (a gradient and an image) in one comma-separated list, and uses the position / size shorthand so both properties travel together with the image inside a single background declaration — a very common pattern for hero sections.

How it works step by step / Under the hood

  1. The browser loads the image and determines its intrinsic width, height, and aspect ratio (SVGs without fixed dimensions use their viewBox aspect ratio).
  2. It computes the background positioning area — normally the padding box (content + padding, not border).
  3. It resolves background-size: for cover, it scales the image until both dimensions are greater than or equal to the positioning area’s dimensions, keeping aspect ratio (so the smaller axis exactly fits and the larger axis overflows); for contain, it scales until both dimensions are less than or equal to the area, so the whole image fits with possible empty space on one axis.
  4. It resolves background-position. For a percentage p%, the formula is: offset = (positioning-area-size - scaled-image-size) * p / 100. This is why 50% 50% centers the image (positive or negative leftover space is split evenly) and why 100% 100% aligns the bottom-right — not because of some special-case rule, but simple arithmetic on the leftover space.
  5. If background-repeat is not no-repeat, the sized image is tiled outward from the computed position in the required directions.
  6. Layers are painted back-to-front: the last item in a comma-separated background list is drawn first (furthest back), and earlier items paint on top of it.

Common Mistakes

Mistake 1: Using cover without no-repeat and being surprised nothing looks different

.card {
  background-image: url("pattern.png");
  background-size: cover;
}

This isn’t invalid, but it’s misleading: with cover, the scaled image already fills the entire box, so background-repeat‘s default value of repeat never gets a chance to tile anything (there’s no leftover space to tile into). Developers sometimes then add repeat-related properties expecting a tiling effect and get confused when nothing changes. Being explicit avoids the confusion:

.card {
  background-image: url("pattern.png");
  background-size: cover;
  background-repeat: no-repeat;
}

Mistake 2: Wrong order in the background shorthand

.box {
  background: cover center url("photo.jpg") no-repeat;
}

This is invalid CSS. Inside the background shorthand, size must directly follow position and be separated by a forward slash — you cannot place cover/contain as a free-floating value before the position. The corrected version:

.box {
  background: url("photo.jpg") center / cover no-repeat;
}

Mistake 3: Assuming background-size percentages are relative to the viewport

.thumb {
  width: 100px;
  height: 100px;
  background-image: url("logo.png");
  background-size: 50% 50%;
}

A common misconception is that 50% here means half the viewport or half the image’s natural size. It actually means 50% of the background positioning area (the element’s padding box) — so on this 100x100px box, the image is rendered at exactly 50x50px. Always reason about percentages relative to the element itself, not the page.

Best Practices

  • Use background-size: cover for full-bleed hero and banner images where cropping is acceptable, and contain for logos or icons that must never be cropped.
  • Always pair a specific background-position (like center 20%) with cover on photos with an important focal point, so cropping trims the edges rather than the subject.
  • Add background-repeat: no-repeat whenever you use cover/contain or an offset position, to avoid relying on implicit tiling behavior.
  • Set a background-color fallback close to the image’s dominant color so there is no jarring flash or gap while the image loads or if it fails to load.
  • Prefer the single-line background: ... center / cover no-repeat; shorthand for simple cases, but fall back to the longhand properties when you need four-value edge offsets or are overriding just one sub-property later in the cascade.
  • Test background images at multiple viewport widths — cover can crop critical content unpredictably on very wide or very narrow screens.

Practice Exercises

  • Create a .banner class, 400px wide and 200px tall, with a background image that uses background-size: cover and is positioned so the top of the image stays visible while the bottom gets cropped as the box’s height shrinks.
  • Style a .logo-box element so a company logo image never gets cropped or stretched, is centered inside the box, and leaves visible padding-colored space around it when the box’s aspect ratio doesn’t match the logo’s — pick the property value that guarantees this.
  • Using four-value background-position, place a small notification icon exactly 8px from the top edge and 8px from the right edge of a 250x100px box, sized to 24x24px, without repeating.

Summary

  • background-position places an already-sized background image within an element’s padding box; background-size determines how large that image is drawn before positioning happens.
  • Percentages in background-position are computed from leftover space (area size − image size), not from the raw element dimensions.
  • cover scales an image up to fully fill its box (possibly cropping); contain scales it to fit entirely inside the box (possibly leaving empty space).
  • Four-value position syntax (e.g. right 12px bottom 12px) lets you offset from any edge without recalculating on resize.
  • In the background shorthand, size must always follow position separated by a slash: position / size.
  • Pair cover/contain and offset positions with background-repeat: no-repeat to avoid unexpected tiling.