DEV Community

Cover image for How to overlay your background images
selbekk
selbekk

Posted on • Edited on • Originally published at selbekk.io

How to overlay your background images

I learned two nice little tricks today, and thought I'd write a short article on them.

The challenge

Often, we have background images that we put text on top of. An example could be a hero section, or the above-the-fold content on basically any marketing site these days.

Some times, we need to improve the contrast between the text and the background image. Sure, we could just change the image itself - but some times that's not an option.

The old and clunky way 👴

There are several ways to solve this, but this is how I learned to do it back in the days. I typically create the following HTML structure:

<div class="image-box">
  <div 
    class="image-box__background" 
    style="--image-url: url('some-image.jpg')"
  ></div>
  <div class="image-box__overlay"></div>
  <div class="image-box__content">
    <h1>Buy our product</h1>
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

I would then make the image-box relatively positioned, all children absolutely positioned inside, and stack them in the order I would like.

What is this --syntax?

Note that we're passing in the image url via something called CSS Custom properties. You might also know them as CSS variables. It's a way to pass values between our HTML and CSS. You can read more about CSS Custom properties on MDN.

The styles required to style this could look like this:

/* 
The container box is relative so we can position stuff inside of it 
*/
.image-box {
  position: relative;
}

/*
The background and overlay need to be absolutely positioned
*/
.image-box__background,
.image-box__overlay {
  position: absolute;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
}

/* 
The background image div sizes and positions the background itself.
It's also at the bottom-most position in our "div stack" (z-index 1)

We set the image url via a CSS custom property, that's set via the style attribute in our HTML
*/
.image-box__background {
  background: var(--image-url) center center no-repeat;
  background-size: cover;

  z-index: 1
}

/* 
The overlay div is just a colored element with some opacity.
It's above the background image in our stack, so it appears to 
darken the image 
*/
.image-box__overlay {
  background: rgba(0, 0, 0, 0.5);

  z-index: 2;
}

/* 
The content div is at the top of our stack. 
We'd probably add some padding or flexbox properties here as well, 
to place the content appropriately
*/
.image-box__content {
  position: relative;

  z-index: 3;

  /* Finally, style and place the content */
  color: white;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
}
Enter fullscreen mode Exit fullscreen mode

A lot of code, but it works pretty well. Here's a CodePen that implements it:

The new cool way! 😎

That was a lot code. Turns out, it doesn't have to be that way.

Let's change our HTML to look like this:

<div class="image-box" style="--image-url(some-image.jpg)">
  <h1>Buy our product</h1>
</div>
Enter fullscreen mode Exit fullscreen mode

That looks a bit simpler, right? Let's implement the CSS as well:

.image-box {

  /* Here's the trick */
  background: linear-gradient(rgba(0,0,0,0.5), rgba(0,0,0,0.5)) , var(--image-url) center center;
  background-size: cover;

  /* Here's the same styles we applied to our content-div earlier */
  color: white;
  min-height: 50vh;
  display: flex;
  align-items: center;
  justify-content: center;
}
Enter fullscreen mode Exit fullscreen mode

Here's a CodePen implementing it:

What's happening here?

As you might have noticed, we now specify two background images:

.image-box {
  background-image: 
    linear-gradient(rgba(0,0,0,0.5), rgba(0,0,0,0.5)), 
    var(--image-url);
}
Enter fullscreen mode Exit fullscreen mode

The first background image is a linear gradient that goes from and to the same color. That color is a semi-transparent black, which works as an overlay for your second background.

And that's it really. If you're feeling clever, you could also pass in the amount of darkening you'd want as a second css variable, for further customization. Or use an actual gradient to make your images pop a bit more.

Using box shadow to achieve the same

Turns out, CSS has several ways of layering "meta-content" on top of a background image. Another way to achieve the same is by using the box-shadow property with a huge spread value and the inset setting.

.image-box {

  /* Here's the trick */
  box-shadow: inset 0 0 0 100vw rgba(0,0,0,0.5);

  /* Basic background styles */
  background: var(--image-url) center center no-repeat;
  background-size: cover;

  /* Here's the same styles we applied to our content-div earlier */
  color: white;
  min-height: 50vh;
  display: flex;
  align-items: center;
  justify-content: center;
}
Enter fullscreen mode Exit fullscreen mode

Here's a CodePen with this implementation as well:

This gives you something we can animate as well (notice what happens when you hover the image), which can be a nice UX delight. You don't have the same control over the gradient, however, so which technique you should choose is depending on the context of your design. It's also been noted that this technique might not be as good for performance, especially on lower end devices. Remember to consider this as well when deciding on your technique.

Thanks for coming to my DEV talk.

Top comments (22)

Collapse
 
rmnvsl profile image
Roman Veselý

Nice usage of custom css properties! But I'm worried about the example with box-shadow - it could be really heavy for mobile devices.

I'd rather avoid that one, and keep it just as an example of how many possible ways we have ;)

Collapse
 
selbekk profile image
selbekk

Yeah I was wondering if it would cause any perf issues. I’ll add a note about it :-)

Collapse
 
larisho profile image
Gab • Edited

This is really great! I just spent waay too long trying to accomplish this using the "old way"--you're a life saver.
Just one question: where do you learn about these sorts of modern CSS approaches? There are a lot of mediocre resources out there (I'm looking at you, W3Schools) and it's hard to know what's worth reading through

Collapse
 
selbekk profile image
selbekk • Edited

Hi! I’m glad you found it useful!

To be honest, most of these techniques I’ve read about in blogs and other non-structured places of learning.

MDN is great as a reference, css-tricks.com is great for picking up techniques, and tympnus is great for inspiration.
and css.christmas is great for, well, small tips and tricks - that was our project for Christmas last year 😄

Collapse
 
larisho profile image
Gab

Thanks for the suggestions!
I have already started reading through css.christmas--very funny! I look forward to reading the rest of it.

Collapse
 
zephys profile image
Marcelo Rodrigues

Thank you. Save me.

Collapse
 
duncanmacgregor profile image
Duncan M. MacGregor

This is very clever! Thank you for sharing.

Just one question, how well supported is the var(--image-url) method?

I can't seem to find it on caniuse.com.

Cheers!

Collapse
 
selbekk profile image
selbekk

It’s not supported in IE, unfortunately, since it doesn’t support css dynamic properties. Otherwise, it’s well supported.

Collapse
 
duncanmacgregor profile image
Duncan M. MacGregor

Ahhh, that darn web browser! haha
That's great news it's supported by modern browsers.
Thanks again, really appreciate the knowledge :-)

Collapse
 
gsotelo23 profile image
G

Awesome! This really helped me a lot!

Collapse
 
selbekk profile image
selbekk

Thanks for letting me know! 🥳

Collapse
 
07zee07 profile image
07Zee07

Somehow it doesn't work for me with grid. I only get the invalid property value on the style="--........

Collapse
 
selbekk profile image
selbekk

sorry to hear! Are you using React or something? I'd love to help, but there's not a lot I can do with the information provided ☹️

Collapse
 
07zee07 profile image
07Zee07

hi!
truth be told this is my first website and I am a bit in the dark. I have nested grids applied and very minimal styles (just some font, plus the necessary for the grid to map out the grid areas)
I just wanted a nice overlay for the background pic so the text would be more readable (and it would look a bit fancier too)

Thread Thread
 
07zee07 profile image
07Zee07

I must apologize.
one word: TYPO
it doesn't matter how many times I read my code I could not find it. I made my partner read it, and bang!
lesson learned!!
people! make others read your code too!!
I'm sorry, it's working fine!!!

Collapse
 
angelorubin profile image
angelo rogerio rubin

What a wonderful article, THANK YOU!

Collapse
 
selbekk profile image
selbekk

Thanks for letting me know! 🎉

Collapse
 
hoffmann profile image
Peter Hoffmann

Could the same result be also archived by CSS filters?

Collapse
 
selbekk profile image
selbekk

Great question! Yeah, you'd think that by adding a filter: brightness(0.5), you'd end up with something similar. Unfortunately, that darkens everything, including the content in front of the background image.

Here's a codepen showing what I mean:

Collapse
 
aamangeldina profile image
aamangeldina

This is really helpful and working perfectly! Thank you so much!

Collapse
 
selbekk profile image
selbekk

I'm glad you found it helpful! :)

Collapse
 
ghersabilell profile image
Bilell Ghersa

Thnx dude that was helpfull