How to CSS-Only Overlays Effect with Box-Shadow

Content overlays are a prominent part of modern web design. They help you hide an element on a web page, and later – with the user’s approval – reveal it, and display extra information or controls, such as buttons behind it.

A typical overlay is semi-transparent, with a solid background color (usually black), and there’s some text or buttons on it for users to see or interact with. After the interaction (clicking or hovering) occurs, the overlay gets removed and reveals the content beneath it.

In this article, we’ll have a look at how to add colored overlay to images by using pure CSS. You can see the final result on the demo below. Hover the images to make the overlays reveal the pokemons. Although this post discusses images, the technique it presents can be safely applied to other content types (such as text blocks) as well.

Avoid Adding Extra HTML Elements

Overlays are frequently created by positioning an extra HTML element with an opacity value less than 1 right above the element to be covered. The problem is that this technique involves the usage of an extra element (or pseudo-element) for the overlay.

If you aren’t an HTML size pedantic, having an extra element for overlay is probably not a big deal, as most likely it won’t tax the bandwidth of any network that much. However having separate style rules for elements & their overlays still harms CSS readability and maintainability.

To keep your code in order, and not to mess your HTML outline up, it’s a better choice to use a CSS-only solution.

Create Overlay with box-shadow

So how can you actually create a CSS-only overlay? With the help of the box-shadow CSS property. The box-shadow is perfect for this job, since what is an overlay but a dark shadow cast over an element?

The box-shadow has a property value called inset, which causes the shadow to appear inwards of the frame of the box.

An inset box-shadow with a shadow size half or more than half of the width and height of the element, creates a shadow that covers the entire element.

.box {
width: 200px;
height: 200px;
box-shadow:  green 0 0 0 100px inset;
Box Shadow Covering Entire Element
Box shadow covering the entire element

Since overlays usually have some transparency, you need to add it to the box shadow as well. This can be done by using the rgba() function for shadow color.

The rgb portion of rgba, represents, red, green and blue color channel values, while a represents the alpha channel, which is responsible for transparency.

For the alpha channel, a value of 1 creates an opaque color, while 0 creates a fully transparent color.

By assigning a value between 0 and 1 to the alpha channel of the rgba color value of the box shadow, you can create a semi-transparent overlay.

Create the Code for the Demo

Our demo will show the images and names of different pokemons. Here we’ll only create the code for Bulbasaur, the first pokemon in the demo, as the others are made the same way (on Codepen you can check out the code for them as well).


For the HTML, we only need to create a box to which we’ll add everything else with CSS.

<div id="bulbasaur" class="pokemon"></div>

In the CSS below, the .pokemon elements display the pokemon images, and the .pokemon::after pseudo-elements carry the name of the pokemon.

Since the box-shadow property can take multiple values in order to render multiple shadows, besides the overlay shadow, I also added other shadows of grey to the .pokemon and .pokemon:hover elements for aesthetics.

/* pokemon pictures */
.pokemon {
  width: 200px;
  height: 200px;

  /* center content using flex box */
  display: flex;
  justify-content: center;
  align-items: center;

  /* overlay */
  box-shadow: 0 0 0 100px inset,
      0 0 5px grey;

  /* hover-out transition */
  transition: box-shadow 1s;

/* pokemon names */
.pokemon::after {
  width: 80%;
  height: 80%;
  display: block;
  font:  16pt 'bookman old syle' ;
  color: white;
  border: 2px solid;
  text-align: center;

  /* center content using flex box */
  display: flex;
  justify-content: center;
  align-items: center;

  /* hover out transition */
  transition: opacity 1s .5s;

/* reveal pokemon picture on hover */
.pokemon:hover {
  transition: box-shadow 1s;
  box-shadow: 0 0 0 5px inset,
  0 0 5px grey,
  0 0 10px grey inset;

/* hide pokemon name on hover */
.pokemon:hover::after {
  transition: opacity .5s;
  opacity: 0;

When the .pokemon elements are hovered, their box-shadow need to change to reveal the image behind.

You can see that the .pokemon:hover selector gets a new box-shadow that removes the overlay, and the .pokemon:hover::after selector hides the name of the pokemon by using the opacity property.

You might have also noticed the absence of color values in the overlay box-shadows in the .pokemon and .pokemon:hover style rules. The overlay box-shadow color of the individual pokemons need to be specified in their own seperate style rules, as they’re all different from each other.

As box-shadow doesn’t have any longhand property, you can’t set its shadow color individually with something like, box-shadow-color; instead – we use the color property.

By default, when you give a value for the color property, that value is applied for the border, the outline and the box-shadow colors as well. So, you can simply use the color property to add color to box-shadow.

#bulbasaur {
  background-image: url(;
  /* color value used for box shadow color */
  color: rgba(71, 121, 94, 0.9) ;

  /* pokemon name */
  content: 'Bulbasaur';

The color of the overlay shadow uses the aforementioned rgba() function with 0.9 for alpha value to make the overlay transparent.

Apart from the color of the overlay box-shadow, the above CSS also adds the rules that are individual to each pokemon – the image as background-image and the name.

And that’s all, we’re ready with our CSS-only colored image overlay. Have a look at the code of all pokemons in the pen below.