Lesson 19: Shadows

Say you’ve been sent a design to build. In that design there’s a picture of a t-shirt, cut out, with a drop shadow. The designer tells you the product image is dynamic and can be updated through the content management system, so the drop shadow needs to be dynamic too. Instead of a t-shirt, the image could be a visor or shorts, or any other item. How do you do that with CSS?

CSS has the box-shadow and text-shadow properties. The picture isn’t text, so you can’t use text-shadow. If you use box-shadow, the shadow sits on the surrounding box, not around the t-shirt.

Luckily, there is another option. It is the drop-shadow() filter. This lets you do exactly what the designer asked for. There are plenty of options when it comes to shadows in CSS, and each one is built for a different job.

Box shadow

The box-shadow property adds shadows to the box of an HTML element. It works on block elements and inline elements.

.my-element {
    box-shadow: 5px 5px 20px 5px #000;
}

The values for box-shadow come in this order.

  1. Horizontal offset. A positive number pushes the shadow out from the left, and a negative number pushes it out from the right.
  2. Vertical offset. A positive number pushes the shadow down from the top, and a negative number pushes it up from the bottom.
  3. Blur radius. A larger number makes a more blurred shadow, and a smaller number makes a sharper shadow.
  4. Spread radius (optional). A larger number makes the shadow bigger, and a smaller number makes it smaller. If you set it to 0, the shadow is the same size as the blur radius.
  5. Color. This can be any valid color value. If you don’t set it, the computed text color is used.

To make a box shadow an inner shadow instead of the default outer shadow, add the inset keyword before the other values.

/* Outer shadow */
.my-element {
    box-shadow: 5px 5px 20px 5px #000;
}

/* Inner shadow */
.my-element {
    box-shadow: inset 5px 5px 20px 5px #000;
}

Multiple shadows

You can add as many shadows as you like with box-shadow. Add a comma separated list of value sets to do this.

.my-element {
  box-shadow: 5px 5px 20px 5px darkslateblue, -5px -5px 20px 5px dodgerblue,
    inset 0px 0px 10px 2px darkslategray, inset 0px 0px 20px 10px steelblue;
}

Properties affecting box-shadow

Adding a border-radius to your box also changes the shape of the box shadow. This is because CSS makes the shadow from the shape of the box, as if light is pointing at it.

.my-element {
  box-shadow: 0px 0px 20px 5px darkslateblue;
  border-radius: 25px;
}

If your box with box-shadow is in a container that has overflow: hidden, the shadow won’t break out of that overflow either.

<div class="my-parent">
  <div class="my-shadow">My shadow is hidden by my parent.</div>
</div>
.my-parent,
.my-shadow {
  width: 250px;
  height: 250px;
}

.my-shadow {
  box-shadow: 0px 0px 20px 5px darkslateblue;
}

.my-parent {
  overflow: hidden;
}

Text shadow

The text-shadow property is a lot like the box-shadow property. It only works on text nodes.

.my-element {
  text-shadow: 3px 3px 3px hotpink;
}

The values for text-shadow are the same as box-shadow, in the same order. The only difference is that text-shadow has no spread value and no inset keyword.

When you add a box-shadow, it’s clipped to the shape of your box. A text-shadow has no clipping. This means that if your text is fully or partly see-through, the shadow shows through it.

.my-element {
  text-shadow: 3px 3px 3px gold;
  color: rgb(0 0 0 / 70%);
}

Multiple shadows

You can add as many shadows as you like with text-shadow, just like with box-shadow. Add a comma separated list of value sets. You can make some really cool text effects this way, such as 3D text.

.my-element {
  text-shadow: 1px 1px 0px white,
    2px 2px 0px firebrick;
  color: darkslategray;
}

Drop shadow

To get a drop shadow that follows the curves of an image, use the CSS drop-shadow filter. This shadow is applied to an alpha mask. That makes it very useful for adding a shadow to a cutout image, like the one in the intro of this module.

.my-image {
  filter: drop-shadow(0px 0px 10px rgba(0 0 0 / 30%))
}

[!IMPORTANT] We cover CSS filters in another module. In short, filters let you apply more than one graphical effect to the pixels of an element.

The drop-shadow filter has the same values as box-shadow, but the inset keyword and spread value are not allowed. You can add as many shadows as you like by adding more drop-shadow values to the filter property. Each shadow uses the last shadow as a positioning reference point.


Adapted from Learn CSS © Google and contributors, licensed under CC BY 4.0 (prose) and Apache 2.0 (code samples).