A Responsive CSS Hero Background Image with Opacity and Color Overlay that Does Not Affect Text
High quality full screen width hero images are a staple in modern web design. Some are more artistic, using a mixture of opacity to give the images more pizzaz.
But what if you could make a stunning CSS responsive background image using transparency and color overlay without affecting text or other overlaying elements.
Traditionally this took some Photoshop skills.
All you need is some HTML and CSS skills to make a full screen background hero image with opacity and a color overlay that does not affect the text overlay.
In this tutorial you will see how to create the full screen image with affects and a text overlay. You can access the source code in the Love2Dev Samples GitHub repository.
Creating a Full Screen Background Hero Image Using HTML and CSS
The example is a demonstration page. It has two sections, the hero image and a second section. The second section is not vital to this demonstration, but provides some extra value to give the final result a 'real page' feel.
First, the hero HTML:
There are only 4 elements to create the example hero image and text overlay. The 'hero' element wraps everything. The 'hero-message' wraps the text.
For the demo I will center the text both horizontally and vertically.
Before we start with the actual hero CSS I added a simple CSS reset to the styles. The wild card rule (*) sets both margin and padding to 0 by default.
The html and body elements have their height and width set to 100%. This makes sure the markup will cover the entire view port.
* { margin: 0; padding: 0; } html, body { width: 100%; height: 100%; }
If you are using a CSS library like Bootstrap or Materialize they include more robust style resets, so you wont need to repeat this code.
The hero image, in our example, fills the entire browser view port, which I assume is also the full screen. Of course the user could use a smaller window on desktops.
To do this the height, width and min-width and min-height are all set to 100%. To make sure the hero aligns with the body element the position is set to relative.
.hero { width: 100%; height: 100%; min-width: 100%; min-height: 100%; position: relative; }
So far we just have an empty rectangle that consumes the available space.
Let's add the background image.
To do this we will leverage a pseudo element.
For several years browsers have supported the concept of pseudo elements. There are 'virtual' elements added to real elements defined in the HTML.
You can style these elements by appending the following selector syntax, ::pseudo-element.
The original syntax uses a single : and most browsers still support that as well.
These are the most common pseudo elements:
- ::after
- ::before
- ::cue
- ::first-letter
- ::first-line
The background hero image uses the ::before and ::after pseudo elements.
First, the actual image.
The .hero::before selector creates a CSS rule that adds the image to the element's background. The background-image property points to the image.
The background-size property is set to cover. This tells the browser to render the image so it covers the entire space available. For the hero image this is the space of the pseudo element, which just happens to be the available view port.
The content property is a special pseudo element property. It lets you define text and other 'content' to be rendered within the pseudo element's container.
Here it is set to an empty string. I have found it a best practice to at least set the pseudo element's content to an empty string to help give it definition. Sort of like adding a character to empty elements.
The next five properties define the position and size of the hero image. Absolute positioning allow us to match the ::before element to the real element's position. Setting top and left to 0 place the ::before element at the same starting point as the real hero element.
Setting the height and width to 100% make it match the hero element's dimensions.
Next, the z-index is set to -2. A negative number was selected to place it behind the real element. I chose -2 to place it behind the color overlay we'll define later.
Finally, add some transparency to the background image by setting the opacity to .4. You can adjust your opacity value to make your desired outcome.
.hero::before { background-image: url(/img/background-1932466_1920-320x212.jpg); background-size: cover; content: ""; display: block; position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: -2; opacity: 0.4; }
At this point we have a full screen, semi-transparent background image using nothing but HTML and CSS.
The problem is the solution references a single image, which may not be optimally sized for the device and view port.
How can we make a responsive background image?
Making a Responsive Background Image
If you noticed, the background image referenced in the default hero::before rule ends with 320x212, 'background-1932466_1920-320x212.jpg'. That is a naming convention I use to tell me the image's dimensions.
I have an AWS lambda function that takes an original image and runs it through a routine that produces a set of optimized responsive images.
The function also produces boilerplate HTML, CSS and JSON I might need to integrate the image into different use cases. The CSS it produces provides a default rule, used in the .hero::before style, referencing the smallest image.
Proper responsive design starts with the smallest view port and progressively adds break points to adjust the layout for larger screens.
After the default rule definition additional CSS rules are defined correlating to larger and larger media query breakpoints.
If you are not familiar with using media queries in CSS this is a brief tutorial.
The hero background image used in this tutorial has a set of different image sizes. Each one is set to be rendered at a width that matches the width of the previous image.
This way the image is larger than the available view port and does not become pixelated when rendered.
@media (min-width: 320px) { .hero::before { background-image: url(/img/background-1932466_1920-460x305.jpg); } } @media (min-width: 460px) { .hero::before { background-image: url(/img/background-1932466_1920-720x477.jpg); } } @media (min-width: 720px) { .hero::before { background-image: url(/img/background-1932466_1920-980x649.jpg); } } @media (min-width: 980px) { .hero::before { background-image: url(/img/background-1932466_1920-1240x821.jpg); } } @media (min-width: 1240px) { .hero::before { background-image: url(/img/background-1932466_1920-1500x993.jpg); } } @media (min-width: 1500px) { .hero::before { background-image: url(/img/background-1932466_1920-1760x1166.jpg); } } @media (min-width: 1760px) { .hero::before { background-image: url(/img/background-1932466_1920-1920x1271.jpg); } }
This technique ensures the image loaded by the device is a best fit, both for the screen dimensions and potential bandwidth usage.
This point we not only have a full screen hero background image with some transparency, but it also uses responsive design techniques to load the best image for the device.
Overlaying Vertically and Horizontally Centered Text
We could have just added the hero image to the background of the main DIV and called it a day. But we want to make it semi-transparent and overlay text.
The problem with applying opacity to an element to affect the background image is there is no way to apply an opacity change to just an element's background. The opacity change applies to everything in the element, including child elements.
To make a semi-transparent background image that does not affect a text overlay we use the ::before pseudo element to display the background image and apply opacity.
Now let's add the text.
The goal is to overlay text that is centered both horizontally and vertically. I also want to make sure it is legible. This means I want a background between the text and the background image. I also want to make the background color semi-transparent.
First, centering the text.
In the past this required some CSS hacks. Today, you can use CSS Flexbox to align elements.
The .flex-center CSS rule changes the parent element's display to flex. From there the justify-content and align-content can be set to center. These two properties align child elements along the X and Y axis.
The flex-direction: column setting it useful for the text overlay because it makes the child elements stack vertically. Otherwise they would naturally align left to right.
.flex-center { display: flex; flex-direction: column; justify-content: center; align-content: center; }
Next, define the rules for the .hero-message wrapper. This element wraps around the text, rendered using H1 and H2 elements.
The .hero-message rule sets the text color, with a slight shadow to make sure it stands out. The elements dimensions are also set to be at least 100% wide and 12em tall.
The height of 12em is relatively arbitrary for this demonstration. You can adjust these dimensions as needed for your needs.
The initial size is 100% wide, designed to fill the available horizontal space on smaller view ports, like phones.
.hero-message { color: #fff; text-shadow: #343a40 2px 2px; min-width: 100%; min-height: 12em; position: relative; }
Next, add a semi-transparent dark background color. We'll use the same techniques we used to render the background hero image. Instead of an image set the color to something dark.
.hero-message::before { content: ""; display: block; position: absolute; margin-left: 0; min-width: 100%; min-height: 12em; z-index: -1; opacity: 0.4; background-color: #343a40; }
Note the dimensions of the ::before pseudo element match the .hero-message element. You want the background to match the real element's size.
Because larger screens won't require a full 100% width, I added a break-point at 1024px to adjust the width to 50%. Notice the background's left margin is set to 25%. This pushes the element to the right 25% to make it match up to the screen's horizontal center.
I could not get the pseudo element to sync its position without the margin hack. If I can figure it out I will update the code and this post.
@media(min-width:1024px) { .hero-message { min-width: 50%; min-height: 12em; } .hero-message::before { margin-left: 25%; min-width: 50%; min-height: 12em; } }
The last set of styles center the text in the header elements. I set the header elements to be the full width of the available space because it was simple. From there you can use the text-align:center to make the text centered.
.hero-title, .hero-sub-title { width: 100%; display: block; text-align: center; } .hero-title { margin: 3% 0; text-transform: uppercase; }
If you want to position the text differently you can play with these settings. Maybe you want to right or left align your text and position it at different places on the background image. It is up to you.
We are not done yet!
Let's add a color overlay.
Adding a Color Overlay
Another common artistic affect I see used in hero images is applying a color tint to the image. For this demonstration I will add a purple color to the image.
This time we'll use the ::after pseudo element.
The same positioning and size rules apply to this element we applied to the background image and text background.
To make sure the color overlay is on top of the background image, set the z-index to -1; Now it should sit between the real element and the background image.
You should set the opacity to make sure the image is visible through the color overlay. I chose .4 again.
You may want to remove the opacity setting on the background image to make it show up clearer through the color overlay.
.hero::after { background-color: #563d7c; content: ""; display: block; position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; z-index: -1; opacity: 0.4; }
Summary
You can use this tutorial as a reference to create beautiful full screen hero background images. The tricks I used can be applied to add transparency and a nice color overlay without affecting text overlay.
Feel free to use this example as template when you need to create rich, full screen hero images with a text overlay.
This is something I have needed on several sites in recent years and struggled to find an HTML and CSS only solution. Now that I solved the problem I hope it helps you out as well.