DEV Community

Cover image for Windows 10 button hover effect using HTML, CSS, and vanilla JS
Jash Gopani
Jash Gopani

Posted on • Updated on

Windows 10 button hover effect using HTML, CSS, and vanilla JS

Table of Contents

  1. Introduction
  2. Observations
  3. Getting Started
  4. The Crux
    1. Calculating the cursor position
    2. Creating the spotlight
    3. Applying spotlight to borders
  5. Additional Resources

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.

Win Button codepen output

Observations

  1. A spotlight that follows the cursor inside the element.
  2. The highlighting of the border according to the cursor position Button Hover

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>
Enter fullscreen mode Exit fullscreen mode

Without CSS, our page looks something like this

image

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;
}

Enter fullscreen mode Exit fullscreen mode

After adding the above CSS styles, we get the following look
Button Hover with CSS only

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
  });
});
Enter fullscreen mode Exit fullscreen mode

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.

Cursor coordinates illustration

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.
Enter fullscreen mode Exit fullscreen mode

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) )
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

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.

  1. 20% 75%: The horizontal and vertical radius of the gradient ellipse shape. % indicates that much % of parent’s width and height respectively.
  2. slice (9): the radial-gradient is our source image for the border and the slice property divides that image into 9 regions which it then applies to edges and corners of the element specified.
  3. width (2px): the thickness of the border-image
  4. outset (2px): the space between the border and the element
  5. 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 `;
  });
});

Enter fullscreen mode Exit fullscreen mode

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! 😁

Additional Resources

You can refer to the additional resources mentioned below for a better understanding of CSS and JS.

  1. MDN Docs - CSS
  2. MDN Docs - JavaScript
  3. CSS Tricks
  4. Border-Image generator tool

Oldest comments (19)

Collapse
 
mysticza profile image
Chris Boik

I always wondered how it could be replicated.
Excellently done, even when viewed on mobile 🤤👍

Collapse
 
jashgopani profile image
Jash Gopani

Thanks a lot, buddy 😀!

Collapse
 
thomasbnt profile image
Thomas Bnt ☕

Cool animation, cool post !

Collapse
 
professor9833 profile image
Professor9833

Excellent post, helped me alot

Collapse
 
sandesh3 profile image
Sandesh Goli

Attractive!!
I loved the way you posted

Collapse
 
jashgopani profile image
Jash Gopani

I am glad you liked it😀, thanks !

Collapse
 
jainamgala777 profile image
jainamgala

Great work. Liked the concept.

Collapse
 
mihirgandhi profile image
Mihir Gandhi

Great tutorial! Looking forward to read more of your work.

Collapse
 
gregoriofrancisco99 profile image
Gregório Francisco

Do U have this in video on YouTube, please?

Collapse
 
jashgopani profile image
Jash Gopani

Unfortunately I don't have one🙈, This is for the first time I have posted any kind of tutorial on internet. Hopefully some day, I'll post on YouTube also😁

Collapse
 
gregoriofrancisco99 profile image
Gregório Francisco

Wow... first time!!! Your article is awesome. The matter you brought was amazing... Very helpful.
You're amazing, man! Keep doin' what U did.

Amazing!!!

Thread Thread
 
jashgopani profile image
Jash Gopani

Thanks a lot, man 🙌. Such support and appreciation keeps me motivated to do more😇

Collapse
 
arnavgu25774294 profile image
Arnav Gupta

Great post! I specially liked the way you divided the article into steps which were difficult at start but then could be easily understood by the reader. Also very nicely presented.

Collapse
 
bhavish089 profile image
Bhavish089

It's Cool ,
but it don't work completely like windows 10 on hovering between two buttons .

Collapse
 
jashgopani profile image
Jash Gopani

Thanks, for the effect between 2 buttons, check the grid hover effect

Collapse
 
badev profile image
Rabbit

this is so cool, pls consider creating content on yt!

Collapse
 
jashgopani profile image
Jash Gopani

Thanks a lot 🙌 Will surely try to come up with more such content

Collapse
 
nermin99 profile image
Nermin Skenderovic

What if my cards have a border radius? Using this example I get square border corners even if I try to specify border-radius.

Collapse
 
ankkaya profile image
Ankkaya • Edited

Great work😀