Backgrounds and borders

Paint a box: layers, gradients, borders, radius and shadow, without reaching for an image.

14of 26
≈ 11Min read
9Parts

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.

eight properties in one
.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.

three layers, top to bottom
.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;
}
Text over a photograph

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

the three kinds
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%);
Fading to transparent

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: text with a transparent text colour — gradient text.
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.

shorthand and sides
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

corners
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 */
Nested radii

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-shadow takes the same syntax without the spread.
  • Animate box-shadow and the browser repaints the whole box every frame. Animate the opacity of a pseudo-element carrying the shadow instead.
x, y, blur, spread, colour
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

two you will reuse
/* 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-image data URI beats a PNG for icons and textures.
  • Large background photographs need the same care as <img>: the right size, a modern format, and image-set() for density.
image-set
background-image: image-set(
  url("/media/hall.avif") type("image/avif"),
  url("/media/hall.jpg") type("image/jpeg")
);

Practice