:hover
) event on specific HTML elements by using the pointer-events
property.
I recently built a team gallery page for one of my clients. Each gallery item is represented by a UI card that uses an overlay that changes color when you move your mouse over it. But I ran into a problem, which inspired this tutorial.
UI Card content & structure
Each gallery card item consists of these elements:
- Employee photo - black/white
- Employee name - white
- Employee role - white
- A subtle dark background overlay
When users move their mouse over each card the background overlay transitions into a light semi-transparent yellow (brand color).
UI Card example
Since I want to keep this tutorial focused on solving a specific problem, here’s a simplified (design-wise) version of how the UI Card looked. Try moving your mouse over it and see the background color transition to a light yellow:
But there is a problem. You might already have spotted it. Try moving your mouse over the text area, do you see it the issue?
As soon as your mouse moves over the text element area, the hover event is canceled — and immediately turns from yellow, back to the default inactive state, with the gray color. Not acceptable!
Here’s the HTML code:
<div class="card">
<div class="card-overlay"></div>
<img
class="card-image"
src="https://lyngholm.dk/static/c2f22e08c2e0281f0c1d543498ccb58a/cad07/Lyngholm_Carsten_okt19_AS_web.jpg"
/>
<div class="card-info">
<div class="card-name">Employee Name</div>
<div class="card-role">Employee Role</div>
</div>
</div>
And the CSS:
.card {
max-width: 350px;
position: relative;
}
.card-overlay {
background-image: linear-gradient(180deg, transparent, rgba(0, 0, 0, 0.5));
height: 100%;
width: 100%;
position: absolute;
top: 0;
left: 0;
transition: background-color 0.6s;
}
.card-overlay:hover {
background-color: rgba(255, 204, 7, 0.5);
}
.card-image {
max-width: 100%;
display: block;
}
.card-info {
position: absolute;
bottom: 2rem;
left: 2rem;
}
.card-name,
.card-role {
color: white;
font-size: 1.5rem;
line-height: 1.25;
padding: 0.25rem;
}
.card-name {
font-weight: bold;
}
So why does the hover event get canceled when you move your mouse over the text area?
Because of the stacking order of the HTML. The stacking order is usually controlled by the CSS z-index
property. However, there is a default stacking order that occurs, if you don’t address it directly with CSS first.
z-index
is a stack order CSS property. The element that has the highestz-index
value (number) is put in front of other elements — assuming theirposition
value is the same. The defaultz-index
value of all elements on a web page isauto
, which is the same as zero0
.
So since we have two elements that are both absolute positioned and they both have a default z-index
of auto
(0) which of them is in front of the other in the stacking order (one has to win)?
The one that comes last in your HTML wins.
In this case, the <div class="card-info">
container that contains the name and role text elements comes after the card-overlay
element:
<div class="card">
<div class="card-overlay"></div>
<img
class="card-image"
src="https://lyngholm.dk/static/c2f22e08c2e0281f0c1d543498ccb58a/cad07/Lyngholm_Carsten_okt19_AS_web.jpg"
/>
<div class="card-info">
<div class="card-name">Employee Name</div>
<div class="card-role">Employee Role</div>
</div>
</div>
On the UI Card example, as soon as you move your mouse over the text elements, you’re no longer hovering the overlay element, but the <class="card-info">
element, which doesn’t have a :hover
(pseudo class) attached. Only .card
has that (see the CSS code).
What can you do?
If you physically move the <div class="card-overlay"></div>
element down below the <div class="card-info>...</div>
element like this:
<div class="card">
<img
class="card-image"
src="https://lyngholm.dk/static/c2f22e08c2e0281f0c1d543498ccb58a/cad07/Lyngholm_Carsten_okt19_AS_web.jpg"
/>
<div class="card-info">
<div class="card-name">Employee Name</div>
<div class="card-role">Employee Role</div>
</div>
<div class="card-overlay"></div>
</div>
Then the stacking order will swap, and this happens:
Great, now the overlay transition is no longer interrupted when you move your mouse over the text area (try it).
But wait... Now that the stacking order has been switched around, the text elements get grayed out by the overlay, which is now in front/on top of it.
We solved a problem while creating another.
Let’s not use that approach, and move the <div class="card-overlay">
element back to its original position.
What if we instead give the .card-overlay
class a z-index
value of 1
?
Now card-overlay
will be put in front of card-info
(which still has z-index: 0
) because it has a higher stack order (z-index: 1
) and now the color change on hover event will again work uninterrupted — which is great, but...
Unfortunately, doing this will result in the the exact same thing as before when we physically moved the <div class="card-overlay">
element below the <div class="card-info">
element. This time we just changed the stacking order directly with CSS. The text elements are still grayed out by the overlay.
This is unacceptable!
Pointer events to the rescue!
I suddenly remembered that CSS has a property called pointer-events
which allows you to decide whether or not an element should react to pointer events.
Pointer events are anything that has to do you your mouse, such as clicking, scrolling, or moving your mouse over (hover) an element.
So I simply added pointer-events: none;
to my .card-info
class, like his:
.card-info {
position: absolute;
bottom: 2rem;
left: 2rem;
pointer-events: none; /* the fix! */
}
And now you can hover over the entire UI Card, including the text, without canceling the hover event — and the text elements retain their 100% white color:
Check out the code CodePen.