The background shorthand
The slash separates position from size, and the size must come after it. Writing the longhands is clearer when there is more than one thing to say.
.panel {
background: #101114 url("/media/grain.png") no-repeat center / cover;
/* colour image repeat position / size */
}
Layers
background-image takes a list. The first is on top, which is the opposite of what most people expect. The background colour is always painted last, underneath everything.
.hero {
background-image:
linear-gradient(rgb(0 0 0 / .65), rgb(0 0 0 / .2)), /* the scrim, on top */
url("/media/texture.png"),
url("/media/photo.jpg"); /* the photo, underneath */
background-size: auto, 180px, cover;
background-repeat: no-repeat, repeat, no-repeat;
background-color: #101114;
}
A gradient scrim layered over the image is how you make text readable without dimming the whole picture. Darken where the text is and leave the rest alone.
Gradients
background: linear-gradient(180deg, #14161a, #0a0b0d);
background: linear-gradient(to right, #14161a 0%, #2a2d35 60%, #0a0b0d 100%);
background: radial-gradient(closest-side circle at 30% 20%, #2a2d35, transparent);
background: conic-gradient(from 90deg, #14161a, #2a2d35, #14161a);
/* a hard stop: two colours at the same position — a stripe, not a fade */
background: linear-gradient(90deg, #c2452d 50%, #14161a 50%);
linear-gradient(#000, transparent) fades through transparent black in sRGB, which can look grey in the middle. Write the end colour explicitly — rgb(0 0 0 / 0) — or interpolate in a better space: linear-gradient(in oklab, #000, transparent).
Size, position and origin
Check the contrast of gradient text carefully — the lightest part of the gradient is the part that has to pass.
cover— fill the box, crop what does not fit.contain— fit entirely inside, leaving gaps.background-position: 50% 30%— which part stays in frame when cropped.background-attachment: fixed— the image stays put while the page scrolls. Expensive, and it behaves badly on iOS.background-clip: textwith a transparent text colour — gradient text.
.gradient-text {
background: linear-gradient(90deg, #f1521f, #f2a03f);
background-clip: text;
-webkit-background-clip: text;
color: transparent;
}
Borders
A translucent border works on any background; a hard-coded one works on exactly the background you designed it for.
border: 1px solid rgb(255 255 255 / .12);
border-block-end: 1px solid var(--line); /* logical: flips in RTL */
border-style: dashed; /* solid, dashed, dotted, double, none */
Radius
border-radius: 12px;
border-radius: 12px 12px 0 0; /* TL TR BR BL */
border-radius: 50%; /* a circle, on a square box */
border-radius: 999px; /* a pill, on any box */
border-radius: 40% 60% 55% 45% / 50% 45% 55% 50%; /* an organic blob */
A rounded box inside a rounded box looks wrong with the same radius. The inner one should be the outer radius minus the padding: calc(12px - 6px).
Shadows
- Real shadows are soft and offset downwards. One tight, dark shadow at 0 0 looks like a glow, not a light.
- An inset highlight on the top edge is what makes a dark panel read as glass.
text-shadowtakes the same syntax without the spread.- Animate
box-shadowand the browser repaints the whole box every frame. Animate theopacityof a pseudo-element carrying the shadow instead.
box-shadow: 0 1px 2px rgb(0 0 0 / .08);
/* several layers read as one soft, believable shadow */
box-shadow:
0 1px 1px rgb(0 0 0 / .04),
0 4px 8px -2px rgb(0 0 0 / .08),
0 16px 32px -8px rgb(0 0 0 / .12);
/* inset: drawn inside the box */
box-shadow: inset 0 1px 0 rgb(255 255 255 / .1);
/* a shadow that follows a transparent shape, not the box */
filter: drop-shadow(0 8px 16px rgb(0 0 0 / .3));
Patterns without an image
/* graph paper */
.grid-bg {
background-image:
linear-gradient(rgb(255 255 255 / .06) 1px, transparent 1px),
linear-gradient(90deg, rgb(255 255 255 / .06) 1px, transparent 1px);
background-size: 24px 24px;
}
/* diagonal stripes */
.stripes {
background: repeating-linear-gradient(
45deg, #14161a 0 8px, #1b1e24 8px 16px
);
}
Weight
- A gradient or a pattern in CSS is bytes, not a file. Prefer them to images for anything geometric.
- An SVG used as a
background-imagedata URI beats a PNG for icons and textures. - Large background photographs need the same care as
<img>: the right size, a modern format, andimage-set()for density.
background-image: image-set(
url("/media/hall.avif") type("image/avif"),
url("/media/hall.jpg") type("image/jpeg")
);