DEV Community

loading...
Cover image for How to overlay your background images

How to overlay your background images

selbekk profile image selbekk Originally published at selbekk.io Updated on ・4 min read

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>

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;
}

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>

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;
}

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);
}

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;
}

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.

Discussion

pic
Editor guide
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 Author

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

Collapse
larisho profile image
Gab

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 Author

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 Author

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 Author

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 Author

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 Author

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 Author

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 Author

I'm glad you found it helpful! :)

Collapse
ghersabilell profile image
Bilell Ghersa

Thnx dude that was helpfull