Lesson 22 / 26
Backgrounds
Background layers, the calculation behind percentage positioning, repeat modes, and the numeric counterpart of cover and contain scaling.
Contents
The text decisions are complete. The back of the boxes has not been looked at yet. The Box Model lesson established that the background extends to the border box and covers the padding; what the background itself can be was not said.
A background is not just a color. It can be a layer stack made of positionable, repeatable, scalable images. This lesson takes up those layers’ rules and calculations.
A Background Is a Layer Stack
background is a shorthand and sets eight longhands: -color,
-image, -position, -size, -repeat, -origin, -clip,
-attachment.
background-image can take multiple comma-separated values. Each
value establishes a layer, and the layers get ordered left to
right, top to bottom: the first member of the list gets drawn on
top.
background-color is not a layer; it always gets drawn at the very
bottom, as a single color. This is why it cannot get multiplied with
a comma.
The other longhands also get multiplied with a comma and get matched
to the layers in order. The number of layers is determined by the
length of the background-image list; lists that fall short get
repeated from the start.
A Percentage Position Is an Alignment
When background-position takes a percentage, that percentage is not
a distance, it is an alignment: the image’s percent point gets
matched to the area’s percent point. The result:
// background.mjs — background-position percentage and repeat math // percentage positioning: the image's p% point lands on the area's p% point function position(areaWidth, imageWidth, value) { if (typeof value === "number") return value; // px const p = parseFloat(value) / 100; return (areaWidth - imageWidth) * p; } const AREA = { width: 400, height: 200 }; const IMAGE = { width: 120, height: 60 }; console.log(`area ${AREA.width}x${AREA.height}, image ${IMAGE.width}x${IMAGE.height}`); for (const [x, y] of [["0%","0%"], ["50%","50%"], ["100%","100%"], ["25%","75%"], [16, 16]]) { const left = position(AREA.width, IMAGE.width, x); const top = position(AREA.height, IMAGE.height, y); console.log(`position: ${String(x).padEnd(5)} ${String(y).padEnd(5)} -> top-left (${left}, ${top})`); } console.log("\n--- repeat: tile starting points ---"); function tiles(area, image, start, repeatMode) { if (repeatMode === "no-repeat") return [start]; const points = []; let x = start; while (x > -image) x -= image; // rewind leftward x += image; while (x < area) { points.push(x); x += image; } return points; } for (const mode of ["repeat", "no-repeat"]) { const points = tiles(AREA.width, IMAGE.width, position(AREA.width, IMAGE.width, "50%"), mode); console.log(`${mode.padEnd(10)} -> ${points.length} tile${points.length === 1 ? "" : "s"}, x = ${points.join(", ")}`); } console.log("\n--- repeat: leftover horizontal space ---"); for (const areaWidth of [400, 360, 480]) { const whole = Math.floor(areaWidth / IMAGE.width); const remainder = areaWidth - whole * IMAGE.width; const roundScale = areaWidth / Math.round(areaWidth / IMAGE.width); console.log(`area=${areaWidth}: repeat -> ${whole} whole + ${remainder}px cropped | round -> ${Math.round(areaWidth / IMAGE.width)} tiles, each ${roundScale.toFixed(2)}px | space -> ${whole} tiles, ${whole > 1 ? (remainder / (whole - 1)).toFixed(2) : 0}px between`); } console.log("\n--- background-size: cover and contain ---"); function scale({ areaWidth, areaHeight, imageWidth, imageHeight }, mode) { const scaleX = areaWidth / imageWidth, scaleY = areaHeight / imageHeight; const factor = mode === "cover" ? Math.max(scaleX, scaleY) : Math.min(scaleX, scaleY); return { width: +(imageWidth * factor).toFixed(1), height: +(imageHeight * factor).toFixed(1), factor: +factor.toFixed(3) }; } const dims = { areaWidth: 400, areaHeight: 200, imageWidth: 800, imageHeight: 600 }; for (const mode of ["cover", "contain"]) { const result = scale(dims, mode); console.log(`${mode.padEnd(8)} factor=${result.factor} -> ${result.width}x${result.height} (area ${dims.areaWidth}x${dims.areaHeight})`); }
area 400x200, image 120x60 position: 0% 0% -> top-left (0, 0) position: 50% 50% -> top-left (140, 70) position: 100% 100% -> top-left (280, 140) position: 25% 75% -> top-left (70, 105) position: 16 16 -> top-left (16, 16) --- repeat: tile starting points --- repeat -> 5 tiles, x = -100, 20, 140, 260, 380 no-repeat -> 1 tile, x = 140 --- repeat: leftover horizontal space --- area=400: repeat -> 3 whole + 40px cropped | round -> 3 tiles, each 133.33px | space -> 3 tiles, 20.00px between area=360: repeat -> 3 whole + 0px cropped | round -> 3 tiles, each 120.00px | space -> 3 tiles, 0.00px between area=480: repeat -> 4 whole + 0px cropped | round -> 4 tiles, each 120.00px | space -> 4 tiles, 0.00px between --- background-size: cover and contain --- cover factor=0.5 -> 400x300 (area 400x200) contain factor=0.333 -> 266.7x200 (area 400x200)
The first block’s 50% 50% value giving 140 and 70 is the
calculation’s direct result: half the difference between the area and
the image. The result is the image getting centered. 100% 100%
pushes it to the bottom-right corner, 0% 0% to the top-left.
The difference with pixel values shows up in the last row: 16px 16px is always 16 units from the edge, independent of the area’s and
image’s dimensions. A percentage declares an alignment, a length
declares a distance.
background-position also accepts a four-value writing, which
selects which edge to measure from: the writing right 16px bottom 16px places it 16 units in from the bottom-right corner.
Repeat Modes
The second block shows that in repeat mode, tiling starts
outside the visible area: the first tile sits at .
Repetition is defined as extending the positioned tile to infinity in
both directions; whatever falls outside the visible area gets
cropped.
The third block gives the difference between the three modes in numbers. A 120-unit image in a 400-unit area:
repeat— 3 whole tiles fit, a 40-unit remainder appears cropped.round— the tile count gets rounded and the image gets scaled: 3 tiles, each 133.33 units. No cropping remains, but the image’s aspect ratio breaks.space— the tile count gets rounded down and the leftover space gets distributed between the gaps: 3 tiles, 20 units apart each. The scale is preserved, a gap opens up.
When the area is an exact multiple of the image (the 360 and 480 rows), all three modes give the same result.
repeat-x and repeat-y repeat on only one axis; no-repeat does
not repeat at all.
cover and contain
The last block compares the two scaling keywords. An 800×600 image in a 400×200 area:
cover — picks the larger of the two axes’ scale ratios. The
result is 400×300: the image fills the area completely, and the
overflowing part gets cropped.
contain — picks the smaller one. The result is 266.7×200: the
whole image is visible, and part of the area stays empty.
The decision gets made by this question: is cropping acceptable, or
is leftover space? cover gets written for a decorative image,
contain for an image that needs to be seen in full.
When cover gets used, where the cropping happens gets directed with
background-position; if the image’s important part is near an
edge, centered cropping cuts it off.
The Background’s Boundaries
Three properties determine which region the background relates to, and the region names from the Box Model lesson appear here as values.
background-origin — chooses which box positioning gets measured
relative to. Its initial value is padding-box.
background-clip — chooses how far the background gets drawn.
Its initial value is border-box; this is why the background shows
through the gaps of a dashed border. When content-box gets written,
the background does not enter the padding.
background-attachment — chooses whether the background scrolls
together with the box (scroll, local) or gets fixed to the
viewport (fixed).
Image Backgrounds and Accessibility
A background image does not exist in the document: it has no alternative text, does not get declared to assistive technology, and does not get read as content by a search engine. This is the same boundary as the one for content produced with pseudo-elements.
The consequence is a rule: no image that carries meaning gets
placed as a background. A location map has to stand in the document
as an img element with alternative text. Backgrounds are for
decorative textures and surfaces that text gets written over.
The legibility of text written over a base image also needs to get secured. If the image does not download or its download is delayed, the text faces the background color; the contrast between that color and the text color has to pass the criterion calculated in the previous lesson.
/* station.css — step 21: background layers */ .masthead { background-color: var(--surface); background-image: url("/images/topography.svg"); background-position: right -40px top 50%; background-repeat: no-repeat; background-size: 320px auto; } .measurement-table tbody tr:nth-child(odd) { background-color: hsl(var(--hue) 18% 97%); background-clip: padding-box; } .warning-box { background-color: hsl(0 66% 97%); background-image: url("/images/warning.svg"); background-position: 12px center; background-repeat: no-repeat; background-size: 20px 20px; padding-inline-start: 44px; }
The pattern in the masthead is decorative and gets written together
with background-color: if the image does not download, the surface
color remains, and the heading text’s contrast stays intact.
The icon in the .warning-box class is decorative too; the warning
itself is written in the text inside the box. The icon is only a
visual marker for the text, and padding-inline-start makes room for
it — the icon not overlapping the text depends on this declaration.
Summary
background-imagebuilds a layer stack by getting multiplied with a comma; the first member of the list gets drawn on top, andbackground-coloralways stays at the bottom, as a single color.- A percentage background position is an alignment, not a distance:
the image’s percent point gets matched to the area’s
percent point; this is why
50%centers. - Repeat modes handle the leftover space differently:
repeatcrops it,roundremoves the crop by scaling the image,spacedistributes the leftover between the tiles. coverpicks the larger of the two scale ratios, fills the area, and crops the overflow;containpicks the smaller one, shows the whole image, and leaves empty space.- A background image does not exist in the document and does not get declared to assistive technology; images that carry meaning belong in the markup, backgrounds get written only as a decorative layer.
Next Step
Background layers got built with images coming from a file. A layer can also get produced without a file: with calculated transitions between colors. The next lesson takes up linear and radial gradients, the calculation behind color stops, and why gradients count as a background image, not an image.
To keep your progress and take notes, Log in
My notes
Log in to take notes.