TL;DR: I created a CodePen demo which shows how to create seamlessly-faded background images with CSS gradient overlays.
The problem
Let’s say you are designing a tabbed user interface and you’d like to show a different background image for each tab, which fades seamlessly into the background of the page. How would you do that without using any image editing software – Photoshop, GIMP etc.?
The solution
One possible option is to use CSS gradients and somehow overlay the background images with a gradient, which starts with a transparent color and ends with the same color as the color of your background. Because of accessibility, you’d probably want to include the images as actual <img>
elements in the HTML and not use them though the background-image CSS property.
I had a similar challenge the other day and it turned out, I had to pull out some tricks to get to a reliable solution. It is not the perfect solution, but it solved my problems. So, here we go…
I’m using Bootstrap 3. The HTML structure I have consists of:
- one wrapping .container
<div>
, - one .nav-tabs unordered list (
<ul>
) for the tab navigation and - one .tab-content
<div>
— which includes the content for each tab in a wrapping .tab-pane<div>
.
This is basically the default Bootstrap 3 structure of the Togglable tabs component.
As you might already know, the Bootstrap .container <div>
has limited width and does not cover the whole width of the page — if it’s not .container-fluid. That’s why I set a background color to the <body>
element. I’m intentionally not using .container-fluid here because I want to add extra empty space to the sides of the content, so that the tab backgrounds are more visible.
Next thing, place the background images within the .tab-content wrappers!
Plan A
I’m including a different image in each tab and targeting all of these images through a specific CSS class — I additionally define styling rules to stretch these images to the full width and height of the page, by converting them to absolutely positioned elements.
So, now I have a different background for each tab, which is shown only when the tab is active – toggled visible. How do I add the CSS gradient on top? Well, that’s easy, I thought… I can just play with the :before
and :after
pseudo elements and define a new absolutely positioned block element over each image… a nice and clean DRY solution… perfect!
As you might have guessed, that didn’t go too well… and the reason is: the W3C CSS specification does not fully define the interaction of :before
and :after
with replaced elements (such as IMG in HTML). This will be defined in more detail in a future specification.
So much for my perfect solution…
Plan B? Of course!
I will overlay each tab background with an additional absolutely positioned <div> element with a CSS gradient set as its background. I will also add another wrapping <div>
so that I can specify z-index and keep the tab background and gradient overlay in the back. The solution will obviously be less DRY, but hey… I’m fine with it, as long as I don’t need to worry about image editing.
I certainly did not expect that Plan B will fail as well.
The CSS gradient was not applied over the tab background and I didn’t have a clue why. I tried setting z-index
to both the tab background and the gradient overlay… didn’t change anything. Then I went back to the :before
and :after
solution, just in case there were some newer W3C specifications I somehow missed. In reality, I just wasted time… and as if that wasn’t enough, I tried even more stupid things, which I’m not going to describe here for obvious reasons. Then, it finally strike me! Maybe the order of the DOM elements should be different?
You see… in my Plan B implementation I always placed the <img>
element below the CSS gradient background. This is how Photoshop layers work — you place what you want on top, higher in the structure. Since I wanted my CSS gradient to cover the tab background, I, logically, placed the gradient over the <img>
element. But, those rules do not apply to the order of the DOM elements. The order of the DOM elements goes in the reverse direction — from top to bottom, so if you want one absolutely-positioned block element to cover another one, you must place it after the element you’d like covered — that was the missing piece in my puzzle.
Are we finally there yet? Yes we are! Plan B is working.
See the Pen Seamlessly-faded Backgrounds with CSS Gradients by Goce Mitevski (@gocemitevski) on CodePen.
Usability and Maintainability
I can think of many scenarios where the Plan B implementation might be relevant, such as:
- Featured images in WordPress themes,
- Section-based landing pages,
- Any block element which needs to have a faded background image…
When it comes to maintainability, more work is obviously needed to satisfy the DRY principle. Things could have been a lot simpler if the W3C specification for :before
and :after
pseudo-elements was complete and if I finished according to my first plan — Plan A.
Hopefully, the specification for :before
and :after
pseudo-elements will be completed in the very near future and I can update my solution. Until then, thanks for reading.