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

Jash Gopani @jashgopani

About: Self taught developer

Location:
Raleigh, North Carolina, USA
Joined:
May 25, 2020

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

Publish Date: Apr 23 '21
148 19

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

Comments 19 total

  • Chris Boik
    Chris BoikApr 24, 2021

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

  • Thomas Bnt
    Thomas BntApr 24, 2021

    Cool animation, cool post !

  • Professor9833
    Professor9833Apr 24, 2021

    Excellent post, helped me alot

  • Sandesh Goli
    Sandesh GoliApr 25, 2021

    Attractive!!
    I loved the way you posted

    • Jash Gopani
      Jash GopaniApr 25, 2021

      I am glad you liked it😀, thanks !

  • jainamgala
    jainamgalaApr 25, 2021

    Great work. Liked the concept.

  • Mihir Gandhi
    Mihir GandhiApr 25, 2021

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

  • Gregório Francisco
    Gregório FranciscoMay 2, 2021

    Do U have this in video on YouTube, please?

    • Jash Gopani
      Jash GopaniMay 2, 2021

      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😁

      • Gregório Francisco
        Gregório FranciscoMay 2, 2021

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

        • Jash Gopani
          Jash GopaniMay 3, 2021

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

  • Arnav Gupta
    Arnav GuptaMay 8, 2021

    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.

  • Bhavish089
    Bhavish089Oct 15, 2021

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

    • Jash Gopani
      Jash GopaniOct 20, 2021

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

  • Rabbit
    RabbitOct 29, 2021

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

    • Jash Gopani
      Jash GopaniNov 7, 2021

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

  • Nermin Skenderovic
    Nermin SkenderovicNov 24, 2022

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

  • Ankkaya
    AnkkayaDec 27, 2023

    Great work😀

Add comment