Module: CSS
CSS·035·4 MIN READ

035: Borders, Shadows, Opacity, Images, and Filters

TOPICS COVERED: Borders, Shadows, Opacity, Images, and Filters

Learning outcomes

By the end, you can use borders and outlines for structure and focus; create restrained box/text shadows; distinguish opacity from alpha colors; size/crop images with object-fit; use CSS filters and backdrop filters responsibly; and debug visual effects that create stacking or performance issues.

Prerequisites and retrieval

Retrieve backgrounds from 021, box model from 023, positioning/stacking from 026, and responsive design from 030.

Borders

css
.card {
  border: 1px solid #cbd5e1;
}

Longhands:

css
.card {
  border-width: 1px;
  border-style: solid;
  border-color: #cbd5e1;
}

Logical side:

css
.notice {
  border-inline-start: 4px solid #2563eb;
}

Radius:

css
.card {
  border-radius: 0.75rem;
}

A large radius can produce a pill when the box is sufficiently short:

css
.tag {
  border-radius: 999px;
}

Outline versus border

css
.button:focus-visible {
  outline: 3px solid #f59e0b;
  outline-offset: 3px;
}

Outlines do not usually consume box-model space and are excellent for focus. Do not use a subtle border color as the only keyboard focus indicator if it is hard to perceive.

Box shadows

Syntax:

css
.card {
  box-shadow:
    0 1px 2px rgb(15 23 42 / 0.08),
    0 8px 24px rgb(15 23 42 / 0.08);
}

Think in parameters:

x-offset y-offset blur spread color.

Inset:

css
.input {
  box-shadow: inset 0 1px 2px rgb(15 23 42 / 0.08);
}

Shadows should communicate elevation or focus hierarchy, not become visual noise.

Worked example: accessible card elevation

css
.card {
  border: 1px solid #e2e8f0;
  border-radius: 0.75rem;
  box-shadow: 0 1px 3px rgb(15 23 42 / 0.08);
  background: white;
}

.card:hover {
  box-shadow: 0 8px 24px rgb(15 23 42 / 0.12);
}

.card:focus-within {
  outline: 3px solid #2563eb;
  outline-offset: 3px;
}

The border still defines the card if shadows disappear in a high-contrast environment.

Text shadow

css
.hero h1 {
  text-shadow: 0 2px 8px rgb(0 0 0 / 0.35);
}

Do not rely on shadow to rescue unreadable image/text contrast. Add a background overlay or choose a better image.

Opacity versus alpha

Entire element:

css
.disabled-card {
  opacity: 0.55;
}

Only background paint:

css
.overlay {
  background: rgb(15 23 42 / 0.55);
}

opacity also creates a stacking context when below 1 and affects descendants. This can produce unexpected layering.

Images: sizing and cropping

css
img {
  max-inline-size: 100%;
  block-size: auto;
}

Fixed crop:

css
.product-image {
  inline-size: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  object-position: center;
}

cover fills and crops. contain fits the entire image and may leave empty space.

Worked example: avatars

css
.avatar {
  inline-size: 3rem;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: 50%;
}

CSS filters

css
.thumbnail {
  filter: grayscale(100%);
}

Common functions:

css
filter:
  blur(2px)
  brightness(0.9)
  contrast(1.1)
  saturate(1.2);

Use filters for presentation. Do not blur/redact sensitive information with CSS and assume the data is protected—the original content still exists.

Backdrop filters

css
.glass-panel {
  background: rgb(255 255 255 / 0.65);
  backdrop-filter: blur(12px);
}

backdrop-filter processes pixels behind the element. It can be visually attractive but expensive and difficult to read. Always provide a sufficiently opaque fallback surface.

css
.glass-panel {
  background: rgb(255 255 255 / 0.92);
}

@supports (backdrop-filter: blur(12px)) {
  .glass-panel {
    background: rgb(255 255 255 / 0.7);
    backdrop-filter: blur(12px);
  }
}

Worked example: image card overlay

html
<article class="story-card">
  <img src="city.jpg" alt="Evening city skyline" width="1200" height="800">
  <div class="story-card__content">
    <h2>City guide</h2>
    <a href="/city">Read guide</a>
  </div>
</article>
css
.story-card {
  position: relative;
  overflow: clip;
  border-radius: 1rem;
}

.story-card img {
  inline-size: 100%;
  block-size: 100%;
  min-block-size: 20rem;
  object-fit: cover;
}

.story-card::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to top,
    rgb(15 23 42 / 0.9),
    transparent 65%
  );
}

.story-card__content {
  position: absolute;
  inset-inline: 1.25rem;
  inset-block-end: 1.25rem;
  z-index: 1;
  color: white;
}

The gradient is decorative, the meaningful title/link remain real HTML, and positioning is used for a deliberate overlay.

Visual-effect performance

Potentially expensive operations include large blurred shadows, filters, backdrop filters, and effects on huge moving regions. Measure actual performance before optimizing.

General principles:

  • keep blur regions bounded;
  • avoid animating blur on large surfaces without testing;
  • avoid dozens of layered shadows;
  • prefer static effects when motion adds little value;
  • test lower-powered devices.

Deep dive: paint, clipping, and image composition

Borders, shadows, images, filters, and opacity mostly affect painting, but they can also change containing blocks or stacking behavior depending on the property.

Multiple backgrounds

CSS can paint multiple background layers:

css
.hero {
  background:
    linear-gradient(
      90deg,
      rgb(2 6 23 / 0.82),
      rgb(2 6 23 / 0.2)
    ),
    url("/images/hero.webp")
      center / cover no-repeat;
}

The first listed background is painted on top. Keep contrast readable even if the image changes.

border-radius and clipping

A rounded border does not automatically guarantee every descendant is clipped exactly how you expect.

css
.card {
  border-radius: 1rem;
  overflow: clip;
}

Use clipping deliberately. Avoid adding overflow: hidden only to make corners look right if the component also needs visible focus rings, sticky descendants, or popovers.

Replaced elements and object-fit

css
.avatar {
  inline-size: 6rem;
  aspect-ratio: 1;
  object-fit: cover;
  object-position: 50% 30%;
}

object-fit controls how replaced content such as an image fits its content box. It does not resize the surrounding layout box by itself.

Alpha versus element opacity

css
/* only the background is translucent */
.notice {
  background: rgb(37 99 235 / 0.12);
}

/* the entire element subtree becomes translucent */
.disabled-preview {
  opacity: 0.5;
}

Using opacity on a container affects text and descendants too. Prefer alpha colors when only one paint layer should be translucent.

Deep dive: filter cost and stacking behavior

Filters can be visually useful:

css
.thumbnail:hover img {
  filter: saturate(1.08) contrast(1.04);
}

But large blurred regions and backdrop-filter can be expensive because the browser may need extra offscreen rendering.

css
.frosted-toolbar {
  background: rgb(255 255 255 / 0.7);
  backdrop-filter: blur(12px);
}

Use them on limited areas, measure on lower-powered devices, and provide a readable base background if the effect is unavailable.

Properties such as filter, opacity < 1, and transforms can create stacking contexts. If an overlay unexpectedly appears behind a neighboring component, inspect stacking contexts before increasing z-index.

Visual debugging checklist

When an image/effect looks wrong, check:

  • the element's actual box dimensions;
  • intrinsic image dimensions;
  • aspect-ratio;
  • object-fit and object-position;
  • overflow/clipping;
  • background layer order;
  • stacking contexts;
  • contrast after filters/overlays;
  • paint cost in DevTools.

Do not solve a geometry problem by piling on visual effects.

Common mistakes

  • Removing outline and replacing it with a barely visible shadow.
  • Using opacity: 0 to hide focusable content.
  • Using CSS blur to “secure” private data.
  • Cropping an essential image so aggressively that meaning is lost.
  • Loading a huge background image for a tiny card.
  • Overusing glass/blur effects that reduce contrast.
  • Creating stacking contexts accidentally with opacity/filter and then fighting z-index.

Practice set

  1. Build three elevation levels with one border plus restrained shadows.
  2. Compare opacity: .5 with an alpha background color.
  3. Create a 1:1 avatar and 16:9 thumbnail using aspect-ratio and object-fit.
  4. Add a filter only on hover, then remove it for reduced-motion if transition is used.
  5. Build a glass panel with an opaque fallback.
  6. Inspect stacking contexts created by opacity and filter.

Recap

Borders describe boundaries, outlines communicate focus, shadows suggest depth, opacity affects the entire box tree, and filters alter rendered pixels. Use visual effects as supporting signals, not as the only source of meaning.

Official references