CSS: Set background image opacity without affecting text

CSS: Set background image opacity without affecting text
Photo by Pankaj Patel / Unsplash

You can't change the opacity of a background image without affecting the opacity of the whole object, but the workaround is simpler than you think!

If you're trying to make text or a child object stand out by fading the background, also reducing the opacity of the foreground content probably defeats the point of changing the opacity in the first place, but we can get around this using the ::before pseudo-element.

This is because ::before is a child of the main element, which means we can target it independently of our other child elements. Imagine you want to set the opacity of the image below:

Before styling ::before

<div id='parent'>
  <div id='child'>
    <strong>Good doggo</strong>
  </div>
</div>
body {margin:0;}

#parent {
	background-image: url('https://images.unsplash.com/photo-1546527868-ccb7ee7dfa6a');
	background-size:cover;
	position: relative;
	height: 100vh;
}

#child { /* #child CSS is purely for styling */ 
 	font-family: sans-serif;
 	font-size: 60px;
 	color: purple;
 	text-align: center;
 	position: relative;
 	top: 50%;
 	left: 50%;
 	transform: translate(-50%, -50%);
}

If you set the opacity of #parent, e.g. opacity: 0.6; you'll end up with faded text as well as the background. The doggo may be good, but the caption is bad.

After using ::before

To fix this, we can add a ::before pseudo-element and move the background-image, background-size & opacity to it.

We'll also need to absolutely position it and set top, bottom, left & right to 0 so it exactly overlaps the parent, and also set content: "" to force the pseudo-element to display.

body {margin:0;}

#parent::before {
	background-image: url('https://images.unsplash.com/photo-1546527868-ccb7ee7dfa6a');
	background-size: cover;
	position: absolute;
	top: 0px;
	right: 0px;
	bottom: 0px;
	left: 0px;
	opacity: 0.6;
	content: "";
}

#parent {

	position: relative;
	height: 100vh;
}

#child { /* #child CSS is purely for styling */ 
 	font-family: sans-serif;
 	font-size: 60px;
 	color: purple;
 	text-align: center;
 	position: relative;
 	top: 50%;
 	left: 50%;
 	transform: translate(-50%, -50%);
}

Shifting this CSS around leaves us with a much more visible title (albeit at the expense of the very patient, good doggo):



Great! Next, complete checkout for full access to techbits.io
Welcome back! You've successfully signed in
You've successfully subscribed to techbits.io
Success! Your account is fully activated, you now have access to all content
Success! Your billing info has been updated
Your billing was not updated