Skip to content
Laviyx Learn

Try

CSS course menu

CSS Shadows, Opacity and Filters: box-shadow, text-shadow, filter and backdrop-filter

Add depth and visual effects with CSS: box-shadow (offsets, blur, spread, inset, layered shadows), text-shadow, opacity, filter functions such as blur, grayscale and brightness, drop-shadow, and frosted glass with backdrop-filter.

Intermediate5 min readUpdated
On this page

Shadows suggest depth, and filters change how an element is drawn. Used lightly, they make interfaces feel layered and polished.

box-shadow#

CSS
box-shadow: offset-x offset-y blur spread color;
CSS
.c { display: inline-block; width: 120px; padding: 20px 0; margin: 14px; text-align: center; background: white; border-radius: 10px; font: 13px monospace; }
HTML
<div class="c" style="box-shadow: 0 1px 3px rgb(0 0 0 / 0.2)">subtle</div>
<div class="c" style="box-shadow: 0 8px 24px rgb(0 0 0 / 0.15)">raised</div>
<div class="c" style="box-shadow: 0 0 0 3px #2f5fe0">ring</div>
<div class="c" style="box-shadow: inset 0 2px 6px rgb(0 0 0 / 0.25)">inset</div>
Result
PartMeaning
offset-x, offset-yHow far the shadow moves right and down (negative values go left and up)
blurHow soft the edge is; 0 is sharp
spreadGrows (or shrinks, if negative) the shadow before blurring
colorUsually black with low opacity
insetDraws the shadow inside the box

A shadow with no offset and no blur but some spread (0 0 0 3px) draws a ring that follows border-radius without changing the element's size, which is useful for focus styles.

Layered shadows#

Real shadows are soft near the object and fade out gradually. Several comma-separated layers look more natural than one:

CSS
.card {
  box-shadow:
    0 1px 2px rgb(0 0 0 / 0.06),
    0 4px 8px rgb(0 0 0 / 0.06),
    0 16px 32px rgb(0 0 0 / 0.08);
}

text-shadow#

text-shadow has no spread or inset: offset-x offset-y blur color.

CSS
.hero { padding: 16px; background: url("/lesson-assets/landscape-600.svg") center / cover; }
.hero h2 { margin: 0; color: white; font: 800 30px system-ui, sans-serif; text-shadow: 0 2px 6px rgb(0 0 0 / 0.6); }
HTML
<div class="hero"><h2>Readable over a photo</h2></div>
Result

opacity#

opacity from 0 (invisible) to 1 (solid) fades an element and all its children. To fade only the background, use a colour with alpha instead (Colors).

filter#

filter applies graphic effects to an element as it is drawn:

CSS
img { width: 120px; height: auto; margin: 4px; border-radius: 6px; }
figure { display: inline-block; margin: 0; text-align: center; font: 12px monospace; }
HTML
<figure><img src="/lesson-assets/landscape-300.svg" alt="" width="300" height="200"><figcaption>none</figcaption></figure>
<figure><img src="/lesson-assets/landscape-300.svg" alt="" width="300" height="200" style="filter: grayscale(1)"><figcaption>grayscale(1)</figcaption></figure>
<figure><img src="/lesson-assets/landscape-300.svg" alt="" width="300" height="200" style="filter: blur(3px)"><figcaption>blur(3px)</figcaption></figure>
<figure><img src="/lesson-assets/landscape-300.svg" alt="" width="300" height="200" style="filter: brightness(0.6)"><figcaption>brightness(0.6)</figcaption></figure>
<figure><img src="/lesson-assets/landscape-300.svg" alt="" width="300" height="200" style="filter: hue-rotate(180deg)"><figcaption>hue-rotate(180deg)</figcaption></figure>
Result

Other functions include contrast(), saturate(), sepia(), invert() and opacity(). Several can be chained: filter: grayscale(1) brightness(1.2);.

drop-shadow#

filter: drop-shadow() follows the shape of the content, including transparent areas, while box-shadow always follows the rectangle:

CSS
img { margin: 12px; }
HTML
<img src="/lesson-assets/mango.svg" alt="" width="100" height="100" style="box-shadow: 6px 6px 8px rgb(0 0 0 / 0.4)">
<img src="/lesson-assets/mango.svg" alt="" width="100" height="100" style="filter: drop-shadow(6px 6px 4px rgb(0 0 0 / 0.4))">
Result

backdrop-filter: frosted glass#

backdrop-filter applies filters to whatever is behind an element, which needs a partly transparent background to be visible:

CSS
.scene { height: 170px; padding: 20px; background: url("/lesson-assets/landscape-600.svg") center / cover; }
.glass {
  max-width: 240px;
  padding: 14px;
  background: rgb(255 255 255 / 0.35);
  backdrop-filter: blur(8px);
  border: 1px solid rgb(255 255 255 / 0.5);
  border-radius: 12px;
  font-family: system-ui, sans-serif;
}
HTML
<div class="scene"><div class="glass"><strong>Frosted glass</strong><p>The hills behind this card are blurred.</p></div></div>
Result

Always check that text on a blurred background still has enough contrast.

Performance and stacking#

Large blurs, many shadows and filters on big areas can make scrolling and animation slower, especially on low-end phones. Also note that filter, backdrop-filter and opacity below 1 create a new stacking context (z-index).

Practice#

Practice exercise

Create a card with a soft two-layer shadow that grows larger when hovered, and a greyscale image inside it that turns to full colour on hover.

Show solution
CSS
.card {
  width: 220px;
  padding: 10px;
  border-radius: 12px;
  background: white;
  box-shadow: 0 1px 2px rgb(0 0 0 / 0.08), 0 4px 10px rgb(0 0 0 / 0.08);
  transition: box-shadow 0.2s;
}
.card:hover { box-shadow: 0 2px 4px rgb(0 0 0 / 0.08), 0 12px 28px rgb(0 0 0 / 0.16); }
.card img { width: 100%; height: auto; border-radius: 8px; filter: grayscale(1); transition: filter 0.3s; }
.card:hover img { filter: none; }
HTML
<div class="card">
  <img src="/lesson-assets/landscape-300.svg" alt="Green hills" width="300" height="200">
  <p>Hover over this card.</p>
</div>
Result

Key takeaway#

Finished this lesson?