035: 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
.card {
border: 1px solid #cbd5e1;
}
Longhands:
.card {
border-width: 1px;
border-style: solid;
border-color: #cbd5e1;
}
Logical side:
.notice {
border-inline-start: 4px solid #2563eb;
}
Radius:
.card {
border-radius: 0.75rem;
}
A large radius can produce a pill when the box is sufficiently short:
.tag {
border-radius: 999px;
}
Outline versus border
.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:
.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:
.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
.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
.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:
.disabled-card {
opacity: 0.55;
}
Only background paint:
.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
img {
max-inline-size: 100%;
block-size: auto;
}
Fixed crop:
.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
.avatar {
inline-size: 3rem;
aspect-ratio: 1;
object-fit: cover;
border-radius: 50%;
}
CSS filters
.thumbnail {
filter: grayscale(100%);
}
Common functions:
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
.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.
.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
<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>
.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:
.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.
.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
.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
/* 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:
.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.
.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-fitandobject-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: 0to 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
- Build three elevation levels with one border plus restrained shadows.
- Compare
opacity: .5with an alpha background color. - Create a 1:1 avatar and 16:9 thumbnail using
aspect-ratioandobject-fit. - Add a filter only on hover, then remove it for reduced-motion if transition is used.
- Build a glass panel with an opaque fallback.
- Inspect stacking contexts created by
opacityandfilter.
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
-
MDN:
object-fit— https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit -
MDN:
filter— https://developer.mozilla.org/en-US/docs/Web/CSS/filter -
MDN: Borders — https://developer.mozilla.org/en-US/docs/Web/CSS/border
-
MDN: Box shadow — https://developer.mozilla.org/en-US/docs/Web/CSS/box-shadow
-
MDN: Opacity — https://developer.mozilla.org/en-US/docs/Web/CSS/opacity
-
MDN:
object-fit— https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit -
MDN: Filter — https://developer.mozilla.org/en-US/docs/Web/CSS/filter
-
MDN: Backdrop filter — https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter
