Table of Contents
Introduction
If you are one of those who are fascinated by the windows 10 hover effect and would like to re-create it then you have come to the right place! In this quick tutorial, I will explain how you can get the same effect using CSS and a little bit of vanilla js.
Before starting with the explanation, let us first have a look at the final result.
Observations
- A spotlight that follows the cursor inside the element.
- The highlighting of the border according to the cursor position
Getting Started
Let us create some items on our page.
HTML
<html>
<head>
<title>Windows hover effect</title>
</head>
<body>
<h1>Windows 10 Button Hover Effect</h1>
<div class="win-grid">
<div class="win-btn" id="1">This is a windows 10 hoverable item</div>
<div class="win-btn" id="2">This is a windows 10 hoverable item</div>
<div class="win-btn" id="3">This is a windows 10 hoverable item</div>
<div class="win-btn" id="4">This is a windows 10 hoverable item</div>
<div class="win-btn" id="5">This is a windows 10 hoverable item</div>
<div class="win-btn" id="6">This is a windows 10 hoverable item</div>
<div class="win-btn" id="7">This is a windows 10 hoverable item</div>
<div class="win-btn" id="8">This is a windows 10 hoverable item</div>
<div class="win-btn" id="9">This is a windows 10 hoverable item</div>
</div>
</body>
</html>
Without CSS, our page looks something like this
CSS
@import url("https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@100&display=swap");
* {
box-sizing: border-box;
color: white;
font-family: "Noto Sans JP", sans-serif;
letter-spacing: 2px;
}
body {
background-color: black;
display: flex;
flex-flow: column wrap;
justofy-content: center;
align-items: center;
}
.win-grid {
border: 1px solid white;
letter-spacing: 2px;
color: white;
display: grid;
grid-template-columns: repeat(2, 1fr);
align-items: stretch;
text-align: center;
grid-gap: 1rem;
padding: 5rem;
}
.win-btn {
padding: 2rem;
text-align: center;
border: none;
border-radius: 0px;
background: black;
color: white;
border: 1px solid transparent;
}
button:focus {
outline: none;
}
After adding the above CSS styles, we get the following look
At this point, we are halfway through the code. We have our target elements set up on DOM, now the only part remaining is applying the highlight effect based on cursor movement.
One thing to note here is that we keep the border of the elements transparent by default and change it based on the cursor position ahead.
The Crux
For each
target element, we need to add event listeners
which listen for mouse movements
. We apply CSS styles when the cursor moves over an element and remove those effects when the cursor leaves an element.
See below how the lines above convert to JS Code
document.querySelectorAll(".win-btn").forEach((b) => {
b.onmouseleave = (e) => {
//remove effects
};
b.addEventListener("mousemove", (e) => {
//add effects
});
});
Notice, I am using the new property based event listener syntax wherever applicable because it is concise and it overrides any other event listeners for the same event on the same object as compared to
addEventListener
which allows you to add multiple event listeners for the same event and same object.
Next, we need to calculate the position of the cursor inside the target element and draw a spotlight circle of a specific radius considering that point as the center of the circle.
Calculating the cursor position
Simple logic to calculate position relative to the element: find the difference of cursor position coordinates and starting coordinates of the target element. Refer to the illustration and code below for a better understanding.
const rect = e.target.getBoundingClientRect();
const x = e.clientX - rect.left; //x position within the element.
const y = e.clientY - rect.top; //y position within the element.
Creating the spotlight
Now simply add a circular radial gradient to our target element with the current cursor position as the center and the colors of the gradient go from white (with low opacity; 0.2) to transparent (opacity 0 basically).
So our radial gradient becomes
radial-gradient(circle at ${x}px ${y}px , rgba(255,255,255,0.2),rgba(255,255,255,0) )
Applying spotlight to borders
The border magic happens when we apply a similar gradient to the border of the image! For such special types of borders, we use the border-image
CSS property as gradient functions in CSS return images! We use the same gradient with slightly more intensity (opacity 0.4).
The syntax for border-image
is as follows
radial-gradient(20% 75% at ${x}px ${y}px ,rgba(255,255,255,0.7),rgba(255,255,255,0.1) ) 9 / 1px / 0px stretch
Now you might be wondering what are these extra values...So let me explain those also...
The syntax for border-image
is
source | slice | border-width | border-outset | slice-repeat
Now you might be wondering what are those extra values with the radial gradient.
- 20% 75%: The horizontal and vertical radius of the gradient ellipse shape. % indicates that much % of parent’s width and height respectively.
-
slice (9): the
radial-gradient
is our source image for the border and theslice
property divides that image into 9 regions which it then applies to edges and corners of the element specified. -
width (2px): the thickness of the
border-image
- outset (2px): the space between the border and the element
- repeat (stretch): this value specifies how the 9 regions, are applied to the image and edges. How the regions 5,6,7,8 specified here are repeated in the border
ℹ: I have attached an amazing tool for playing around with
border-image
(developed my MDN) so you can get a better understanding of this.
At last, we must not forget to remove these styles when the cursor moves out of our element.
Our complete JS code looks like this
document.querySelectorAll(".win-btn").forEach((b) => {
console.log(b);
b.onmouseleave = (e) => {
e.target.style.background = "black";
e.target.style.borderImage = null;
};
b.addEventListener("mousemove", (e) => {
const rect = e.target.getBoundingClientRect();
const x = e.clientX - rect.left; //x position within the element.
const y = e.clientY - rect.top; //y position within the element.
e.target.style.background = `radial-gradient(circle at ${x}px ${y}px , rgba(255,255,255,0.2),rgba(255,255,255,0) )`;
e.target.style.borderImage = `radial-gradient(20% 75% at ${x}px ${y}px ,rgba(255,255,255,0.7),rgba(255,255,255,0.1) ) 1 / 1px / 0px stretch `;
});
});
That's all folks :)
Hope this article has helped you understand, how to breakdown logically an effect into CSS and JS code.
Feel free to comment if you have any questions or issues and I'll try to help you! 😁
- As promised, I have published an article on Windows 10 grid hover effect using HTML, CSS, and vanilla JS. Do check that out also!
Additional Resources
You can refer to the additional resources mentioned below for a better understanding of CSS and JS.