CSS Directional Hover without using JavaScript
This is CSS hover effect from all directions like when you hover from left side of the element, the slide(hover effect) comes from left side. Same way to all other sides top, right, bottom. Its a directional hover effect from all four sides of element.
First lets create a CSS grid.
Create a parent div with id '#grid-plane'. Also Create another parent div inside 'grid-plane' with class name '.holder'. And lets create four child divs with class names .bottom, .left, .right, .top and add spans inside every element.
<div id="grid-plane">
<div class="holder">
<div class="bottom"><span></span></div>
<div class="left"><span></span></div>
<div class="right"><span></span></div>
<div class="top"><span></span></div>
</div>
</div>
Now, copy paste the parent div '.holder' to 15 times so that we get a grid system. (5x4 rows, columns).
<div id="grid-plane">
<div class="holder">
<div class="bottom"><span></span></div>
<div class="left"><span></span></div>
<div class="right"><span></span></div>
<div class="top"><span></span></div>
</div>
<div class="holder">
<div class="bottom"><span></span></div>
<div class="left"><span></span></div>
<div class="right"><span></span></div>
<div class="top"><span></span></div>
</div>
<!-- here copied only 2 times. copy 15 times... -->
</div>
Now, lets create CSS. '#grid-plane' and '.holder'.
body {
font-family: "Lato", sans-serif;
background: #7a7a7a;
}
/* grid-plane - 5x4 rows, columns */
#grid-plane {
display: grid;
width: 510px;
grid-template-columns: repeat(auto-fit, minmax(20%, 1fr));
margin: 100px auto;
}
.holder {
width: 100px;
height: 100px;
position: relative;
overflow: hidden;
border: 1px solid white;
background: #c7c7c7;
}
Lets style the left, top, right, bottom divs.
.left,
.top,
.right,
.bottom {
position: absolute;
z-index: 1;
cursor: pointer;
}
Assign width and height to spans with black background with opacity 0.7.
.left span,
.right span,
.top span,
.bottom span {
position: absolute;
width: 100px;
height: 100px;
background: black;
opacity: 0.7;
z-index: 10;
}
Assign positions. We are moving spans out of '.holder' div initially. They overlay on 'holder' div when we hover on the 'holder' div in their directions.
.left, .left span {
left: -50px;
top: 0;
}
.right, .right span {
left: 50px;
top: 0;
}
.top, .top span {
top: -50px;
left: 0;
}
.bottom, .bottom span {
top: 50px;
left: 0;
}
Lets create ':before' to left, top, right, bottom divs. Initially we are hiding them with opacity 0. This is a square box element and we are rotating it to 45deg and making it look like triangle. We are going to hover on these ':before' elements and we are faking it by hiding it to opacity 0 as if it doesn't exist.
.left:before,
.right:before,
.top:before,
.bottom:before {
position: absolute;
content: "";
background: green;
width: 71px;
height: 71px;
transform: rotate(45deg) translateX(29%);
opacity: 0;
}
This is the main part. Lets create hovers to left, top, right, bottom divs. Now this is very important. If we hover on 'holder' div from any one of the direction like:- If we hover from 'left' side then the span(the black background element with opacity 0.7) will slide from left side(animating) and cover the 'holder' div and also this below code ':before' element will be just behind the span element which is scaled to width and height to 215px and will translateX to -38% which covers the whole 'holder' div. This will block some hover conflicts from rest of the elements(.right, .top, .bottom) divs. These (.right, .top, .bottom) divs hover will not work properly because we have covered the entire 'holder' div with '.left:before' by scaling it to 215px. Same thing happens when we hover from right direction, avoids few conflicts from rest of the divs(.left, .top, .bottom). We can completely avoid all conflicts but we still need to code little more.
.left:hover:before,
.right:hover:before,
.top:hover:before,
.bottom:hover:before {
width: 215px;
height: 215px;
transform: rotate(45deg) translateX(-38%);
}
Remove all divs and keep only .left div inside '.holder' like below screenshot and comment overflow: hidden in '.holder' in CSS to view 'left:before' and span(black background) in browser.
<div id="grid-plane">
<div class="holder">
<div class="left"><span></span></div>
</div>
</div>
This is how it looks if we have all 4 divs in 'holder' div and comment overflow: hidden in CSS.
<div id="grid-plane">
<div class="holder">
<div class="bottom"><span></span></div>
<div class="left"><span></span></div>
<div class="right"><span></span></div>
<div class="top"><span></span></div>
</div>
</div>
Now revert back, undo the above statements. (add all 'holder' parent divs back and uncomment overflow: hidden). Just like 15 times we added 'holder' parent divs earlier.
Lets create hover to the left, top, right bottom divs. Hover effect which covers(overlays) the 'holder' div with the spans(black background with opacity 0.7).
.bottom:hover span {
top: -50px;
animation: movefrombottom 0.25s;
}
.top:hover span {
top: 50px;
animation: movefromtop 0.25s;
}
.left:hover span {
left: 50px;
animation: movefromleft 0.25s;
}
.right:hover span {
left: -50px;
animation: movefromright 0.25s;
}
Lets create animations to the spans of left, top, right, bottom divs.
@keyframes movefromtop {
from { top: -50px; }
to { top: 50px; }
}
@keyframes movefromleft {
from { left: -50px; }
to { left: 50px; }
}
@keyframes movefromright {
from { left: 50px; }
to { left: -50px; }
}
Now we can avoid all hover conflicts by adding below codes.
Now lets hide other divs when we hover 'holder' div from any direction. Lets assume we are hovering from bottom direction. Then we have to hide below divs which are (.left, .right, .top divs). Just hiding all next element siblings.
.bottom:hover + *, .bottom:hover + * + *, .bottom:hover + * + * + * {
display: none;
}
If we hover from left direction. Through below code we are hiding rest of the divs. Hiding below two next element siblings and hiding first div(previousElement sibling) which is above the .left div using (has) class method.
.left:hover + *, .left:hover + * + *, .bottom:has(+ *:hover) {
display: none;
}
If we hover from right direction. Through below code we are hiding rest of the divs. Hiding below one next element sibling and hiding first two divs(.bottom, .left[previousElement siblings]) which is above the .right div using (has) class method.
.right:hover + *, .left:has(+ *:hover), .bottom:has(+ * + *:hover) {
display: none;
}
If we hover from top direction. Through below code we are hiding rest of the divs. Hiding above all previousElement siblings(.bottom, .left, .right) using (has) class method.
.right:has(+ *:hover), .left:has(+ * + *:hover),
.bottom:has(+ * + * + *:hover) {
display: none;
}
We are done with plane grid directional hovers.
Now lets create with images
Copy the <img-details>
section 15 times like we did previously for plane grid.
<div id="grid-images">
<div class="grid-wrapper">
<div class="img-details">
<div class="bottom"><span></span></div>
<div class="left"><span></span></div>
<div class="right"><span></span></div>
<div class="top"><span></span></div>
<div class="card">
<img src="https://shorturl.at/NkOw6" alt="">
</div>
<h3>Click</h3>
</div>
<div class="img-details">
<div class="bottom"><span></span></div>
<div class="left"><span></span></div>
<div class="right"><span></span></div>
<div class="top"><span></span></div>
<div class="card">
<img src="https://shorturl.at/NkOw6" alt="">
</div>
<h3>Click</h3>
</div>
<!-- here copied only 2 times. copy 15 times... -->
</div>
</div>
Just minifying the CSS to make it short here. Also do not delete all the previous CSS. Lets add few more here for images. Here we are just scaling the image and text on hover.
#grid-images {
display: block; width: 500px; margin: 100px auto;
}
.grid-wrapper {
display: grid; padding: 0; grid-gap: 0;
grid-template-columns: repeat(auto-fit, minmax(20%, 1fr));
grid-auto-flow: dense; grid-auto-rows: auto;
background: white; position: relative;
overflow: hidden;
}
.img-details {
display: flex; justify-content: center;
align-items: center; position: relative;
overflow: hidden; width: 100px;
height: 100px; cursor: pointer;
}
.img-details .card {
box-sizing: border-box; box-shadow: none;
border: 0; border-radius: 0; z-index: 0;
width: 100px; height: 100px;
}
.img-details .card > img {
width: 100px; height: 100px;
object-fit: cover; border-radius: 0;
max-width: 100%; z-index: 0;
position: relative; filter: brightness(0.5);
}
.img-details h3 {
text-align: center; position: absolute;
top: 50%; transform: scale(0.5) translateY(-50%);
z-index: 0; opacity: 0; padding: 0;
margin: 0; font-size: 12px; color: white;
letter-spacing: 1px;
}
.img-details:hover h3 {
pointer-events: none;
}
.img-details .card, .img-details .card > img,
.img-details h3 {
transition: 0.25s ease;
}
.right:hover + * + * + * {
opacity: 1; z-index: 1;
transform: scale(1) translateY(-50%);
}
.right:hover + * + * img {
transform: scale(1.15);
}
.left:hover + * + * + * + h3 {
opacity: 1; z-index: 1;
transform: scale(1) translateY(-50%);
-webkit-transform: scale(1) translateY(-50%);
}
.left:hover + * + * + * img {
transform: scale(1.15); -webkit-transform: scale(1.15);
}
.bottom:hover + * + * + * + * img {
transform: scale(1.15); -webkit-transform: scale(1.15);
}
.bottom:hover + * + * + * + * + h3 {
opacity: 1; z-index: 1;
transform: scale(1) translateY(-50%);
-webkit-transform: scale(1) translateY(-50%);
}
.top:hover + div + h3 {
opacity: 1; z-index: 1;
transform: scale(1) translateY(-50%);
-webkit-transform: scale(1) translateY(-50%);
}
.top:hover + * img {
transform: scale(1.15); -webkit-transform: scale(1.15);
}
Now to give hyperlinks, assign to **<img-details>**
div like in below code.
<div id="grid-images">
<div class="grid-wrapper">
<div class="img-details" onclick="location.href='https://www.google.com';"> <!-- hyperlink -->
<div class="bottom"><span></span></div>
<div class="left"><span></span></div>
<div class="right"><span></span></div>
<div class="top"><span></span></div>
<div class="card">
<img src="https://shorturl.at/NkOw6" alt="">
</div>
<h3>Click</h3>
</div>
</div>
</div>
Previously, we had span with black background with opacity 0.7 for hover. Check plain grid for output. Here I changed that span color to `background: #E91E63;` in CSS for hover effect for images.
Output for CSS Directional Hover for Images
Check here for running example : Click Here
Thank you for watching...
Top comments (5)
Neat css trick!
Accessibility tip for future posts you write: Code-in-images cannot be read by people using screenreaders, nor can it be copy/pasted by people wanting to quickly play with a snippet.
In general it's better to use code blocks.
I have modified the tutorial with code snippets and removed the screenshots.
Simple yet soo complex. Super nice tricks 🙌.
Well written BTW 👌.
Nice, but unfortunately breaks if you move the mouse too quickly
The transition speed given is 0.25s. May be you are moving the mouse too fast than that. By doing this the mouse is entering on other elements quickly and also mouse out is happening at same speed on previous elements. That's why it looks like it breaks. It wont break if transition speed is removed but we cannot see the directional hovers from which direction its hovering. It will directly show hover immediately.