DEV Community

Andy Leverenz
Andy Leverenz

Posted on • Originally published at webcrunch.com on

JavaScript mouse-tracking Glow Effect with Tailwind CSS

In this Tailwind CSS tutorial, we'll explore how to add an interactive, mouse-tracking glow effect to web elements using just JavaScript and Tailwind CSS. This simple yet eye-catching effect will enhance the user experience on your webpage.

Prerequisites:

  • Basic knowledge of HTML, CSS, and JavaScript.
  • Tailwind CSS installed in your project.

Setting up a project

I like to use vite.js for projects like this,but you could easily use something else. Vite has built-in support for many things, so it’s more efficient for me to test out new ideas and create tutorials such as this one.

You can reference the docs or follow along as I create a new project. I used bun for package management and vanilla JavaScript to keep things simple. Be sure to remove all the boilerplate Vite.js installs when scaffolding a project.

bunx create-vite # go through the wizard, then cd into your project
Enter fullscreen mode Exit fullscreen mode

Install Tailwind CSS

Tailwind has some dependencies, so we’ll install those using bun. These are saved as dev dependencies because the -d flag passed. You can confirm inside your package.json file post-install.

bun add tailwindcss postcss autoprefixer @tailwindcss/typography -d
Enter fullscreen mode Exit fullscreen mode

Next, we need to generate a tailwind configuration and postcss configuration file. This can be done with the following:

bunx tailwindcss init -p
Enter fullscreen mode Exit fullscreen mode

Add the default Tailwind styles to your stylesheet.

/* style.css */

@tailwind base;
@tailwind components;
@tailwind utilities;
Enter fullscreen mode Exit fullscreen mode

Finally, you can boot your server running:

bun dev
Enter fullscreen mode Exit fullscreen mode

I updated the default tailwind.config.js file to the following for this tutorial. You will want to add directories of files possibly. Read the docs for more information.

/** @type {import('tailwindcss').Config} */
export default {
  content: ["index.html"],
  theme: {
    extend: {},
  },
  plugins: [require("@tailwindcss/typography")],
}
Enter fullscreen mode Exit fullscreen mode

Initial HTML Structure

To make the glow effect more impactful, we can create a custom card that would benefit from the effect in the first place. I chose tropical fish.

You can start by creating a basic HTML structure. Here, we have a div containing a heading, paragraph, and button, wrapped in a parent div with the class glow-capture.

<!doctype html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Create a custom mouse tracking Glow Effect with Tailwind CSS</title>
</head>

<body class="bg-zinc-950 text-zinc-50 antialiased h-screen">
  <div class="max-w-3xl mx-auto px-4 space-y-12 pb-32 pt-16 mt-10 mb-24">
    <!-- Fish 1 -->
    <div class="relative glow-capture">
       <div
        class="group bg-zinc-900/50 border-4 border-white/5 rounded-2xl p-10 shadow-lg shadow-black/80 flex md:flex-row flex-col flex-wrap md:items-start items-center md:justify-between justify-center gap-6 backdrop-blur-md">
        <div class="flex-1 md:order-1 order-2">
          <p class="opacity-50">Tropical fish</p>
          <h2 class="font-bold text-4xl tracking-tighter mb-3">Banana Wrasse</h2>
             <div class="md:prose-base prose-lg prose-zinc prose-invert text-opacity-90">
            <p>The Banana Wrasse is known for its vibrant yellow color and elongated body, resembling a banana.</p>
            <p>This active and playful fish thrives in tropical reef environments, often seen darting among corals and
              rocks.</p>
          </div>

          <button class="font-semibold px-6 bg-zinc-950/50 backdrop-blur-md py-3 rounded-xl text-white/90 border-2 border-white/10 mt-6">Learn more</button>
        </div>
         <img src="fish-1.jpg" alt="Banana Wrasse" class="max-w-sm rounded-full object-cover w-40 h-40 border-4 border-white/5 md:order-2 order-1 shadow-inner" />
      </div>
      <div class="glow-overlay" style="--glow-color: #7c3aed"></div>
    </div>
  </div>
</body>
Enter fullscreen mode Exit fullscreen mode

I’ll start with one section that focuses on one fish for now. We can then duplicate the code and swap some colors, images, and content.

You’ll see some custom classes, such as glow-capture and glow-overlay. These will come in handy when we define some JavaScript and extend Tailwind.

Let’s tackle the CSS first.

Styling with Tailwind CSS

Add the following styles for the .glow-overlay class. These styles create the base for our glow effect. You can leverage the @apply logic with Tailwind CSS here. I kept this code traditional CSS for now.

We use mask and pass a radial-gradient method. It's a fancy way to make gradients that aren't the simpler gradients you might be used to. Read more about masks and radial gradients on mdn.

.glow-overlay {
  --glow-size: 25rem;
  position: absolute;
  inset: 0;
  pointer-events: none;
  user-select: none;
  opacity: var(--glow-opacity, 0);
  mask: radial-gradient(
    var(--glow-size) var(--glow-size) at var(--glow-x) var(--glow-y),
    var(--glow-color) 1%,
    transparent 50%
  );
  transition: 400ms mask ease;
  will-change: mask;
}
Enter fullscreen mode Exit fullscreen mode

Extend Tailwind

We need to extend Tailwind to have a new variant called glow. Read more about plugins and adding variants on the Tailwind CSS documentation.

Here’s my revised tailwind.config.js file.

/** @type {import('tailwindcss').Config} */

const plugin = require("tailwindcss/plugin")

export default {
  content: ["index.html"],
  theme: {
    extend: {},
  },
  plugins: [
    require("@tailwindcss/typography"),
    plugin(
      function ({ addVariant }) {
        addVariant("glow", ".glow-capture .glow-overlay &")
      },
      {
        theme: {
          extend: {
            colors: {
              glow: "color-mix(in srgb, var(--glow-color) calc(<alpha-value> * 100%), transparent)",
            },
          },
        },
      }
    ),
  ],
}
Enter fullscreen mode Exit fullscreen mode

The code uses the plugin extension of the Tailwind CSS library. We pass a new variant called glow, which targets elements within the glow-capture and glow-overlay classes. The & sign acts as a placeholder.

Finally, with the new variant, we can extend the theme based on its usage. To do so, we use the color-mix property in CSS, leveraging the —-glow-color CSS variable that gets defined in our HTML and later manipulated with JavaScript.

Add JavaScript for Mouse Tracking

Write a JavaScript function to track the mouse movement and apply the glow effect. This code listens for mouse movement over the .glow-capture element and updates CSS variables to move the glow effect.

/* main.js */

import "./style.css"

document.addEventListener("DOMContentLoaded", () => {
  const captures = document.querySelectorAll(".glow-capture")

  captures.forEach((capture) => {
    // Clone a child element. For example, we can clone the first child.
    const clonedChild = capture.children[0].cloneNode(true)
    const overlay = capture.querySelector(".glow-overlay")

    // Append the cloned child to the overlay.
    overlay.appendChild(clonedChild)

    capture.addEventListener("mousemove", (event) => {
      const x = event.pageX - capture.offsetLeft
      const y = event.pageY - capture.offsetTop

      overlay.style.setProperty("--glow-x", `${x}px`)
      overlay.style.setProperty("--glow-y", `${y}px`)
      overlay.style.setProperty("--glow-opacity", "1")
    })

    // Add mouseleave event to remove the glow effect
    capture.addEventListener("mouseleave", () => {
      overlay.style.setProperty("--glow-opacity", "0")
    })
  })
})
Enter fullscreen mode Exit fullscreen mode

In my Vite project, a main.js file is the default entry point. I'm just chucking this code there. We do quite a few things in this file:

  1. Listed first for the DOMContentLoaded event and then proceed
  2. Find all elements with the .glow-capture class
  3. Loop through each element with the .glow-capture class and clone it.
  4. The overlay div added in our HTML gets a new child element which effectively doubles our original fish card.
  5. On the individual card, we listen for both the mousemove and mouseleave events, setting custom CSS styles based on the mouse x and y position. If you leave the card with your mouse, we remove opacity, so you can't see the "glow” effect.

Adding the new glow variant classes

Here’s where it gets fun. Because we introduced the new Tailwind CSS variant called glow, we can target elements within the card to change as we navigate the card with our mouse. You can apply the variants like any other Tailwind CSS variants.

<!-- Fish 1 -->
    <div class="relative glow-capture">
      <div
        class="group bg-zinc-900/50 border-4 border-white/5 rounded-2xl p-10 shadow-lg shadow-black/80 flex md:flex-row flex-col flex-wrap md:items-start items-center md:justify-between justify-center gap-6 backdrop-blur-md glow glow:ring-1 glow:border-glow glow:ring-glow glow:bg-glow/[.15]">
        <div class="flex-1 md:order-1 order-2">
          <p class="opacity-50">Tropical fish</p>
          <h2 class="font-bold text-4xl tracking-tighter mb-3 glow:text-glow/[.15]">Banana Wrasse</h2>
          <div class="md:prose-base prose-lg prose-zinc prose-invert text-opacity-90">
            <p>The Banana Wrasse is known for its vibrant yellow color and elongated body, resembling a banana.</p>
            <p>This active and playful fish thrives in tropical reef environments, often seen darting among corals and
              rocks.</p>
          </div>

          <button
            class="font-semibold px-6 bg-zinc-950/50 backdrop-blur-md py-3 rounded-xl text-white/90 border-2 border-white/10 mt-6 glow:ring-1 glow:border-glow glow:ring-glow">Learn
            more</button>
        </div>

        <img src="fish-1.jpg" alt="Banana Wrasse"
          class="max-w-sm rounded-full object-cover w-40 h-40 border-4 border-white/5 md:order-2 order-1 glow glow:ring-1 glow:border-glow/[.5] glow:ring-glow shadow-inner" />
      </div>
      <div class="glow-overlay" style="--glow-color: #7c3aed"></div>
    </div>
Enter fullscreen mode Exit fullscreen mode

Here I’ve applied it to the card, title, button, and fish image to produce a neat effect based on the —glow-color variable passed to the div with the class glow-overlay .

Add more fish to the mix

Here’s my entire HTML file:

<!doctype html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Create a Mouse Tracking Glow Effect with Tailwind CSS</title>
</head>

<body class="bg-zinc-950 text-zinc-50 antialiased h-screen">
  <div class="max-w-3xl mx-auto px-4 space-y-12 pb-32 pt-16 mt-10 mb-24">
    <!-- Fish 1 -->
    <div class="relative glow-capture">
      <div
        class="group bg-zinc-900/50 border-4 border-white/5 rounded-2xl p-10 shadow-lg shadow-black/80 flex md:flex-row flex-col flex-wrap md:items-start items-center md:justify-between justify-center gap-6 backdrop-blur-md glow glow:ring-1 glow:border-glow glow:ring-glow glow:bg-glow/[.15]">
        <div class="flex-1 md:order-1 order-2">
          <p class="opacity-50">Tropical fish</p>
          <h2 class="font-bold text-4xl tracking-tighter mb-3 glow:text-glow/[.15]">Banana Wrasse</h2>
          <div class="md:prose-base prose-lg prose-zinc prose-invert text-opacity-90">
            <p>The Banana Wrasse is known for its vibrant yellow color and elongated body, resembling a banana.</p>
            <p>This active and playful fish thrives in tropical reef environments, often seen darting among corals and
              rocks.</p>
          </div>

          <button
            class="font-semibold px-6 bg-zinc-950/50 backdrop-blur-md py-3 rounded-xl text-white/90 border-2 border-white/10 mt-6 glow:ring-1 glow:border-glow glow:ring-glow">Learn
            more</button>
        </div>

        <img src="fish-1.jpg" alt="Banana Wrasse"
          class="max-w-sm rounded-full object-cover w-40 h-40 border-4 border-white/5 md:order-2 order-1 glow glow:ring-1 glow:border-glow/[.5] glow:ring-glow shadow-inner" />
      </div>
      <div class="glow-overlay" style="--glow-color: #7c3aed"></div>
    </div>

    <!-- Fish 2 -->
    <div class="relative glow-capture">
      <div
        class="bg-zinc-900/50 border-4 border-white/5 rounded-2xl p-10 shadow-lg shadow-black/80 flex md:flex-row flex-col flex-wrap md:items-start items-center md:justify-between justify-center gap-6 backdrop-blur-md glow glow:ring-1 glow:border-glow glow:ring-glow glow:bg-glow/[.15]">
        <div class="flex-1 md:order-1 order-2">
          <p class="opacity-50">Tropical fish</p>
          <h2 class="font-bold text-4xl tracking-tighter mb-3 glow:text-glow/[.15]">Anthia</h2>
          <div class="md:prose-base prose-lg prose-zinc prose-invert text-opacity-90">
            <p>Anthias fish are renowned for their dazzling, multi-hued scales, ranging from pinks and purples to
              oranges
              and yellows.</p>
            <p>
              They form dynamic, shimmering schools in coral reefs, adding a burst of color and life to the underwater
              world.</p>
          </div>
          <button
            class="font-semibold px-6 bg-zinc-950/50 backdrop-blur-md py-3 rounded-xl text-white/90 border-2 border-white/10 mt-6 glow:ring-1 glow:border-glow glow:ring-glow">Learn
            more</button>
        </div>

        <img src="fish-2.jpg" alt="Banana Wrasse"
          class="max-w-sm rounded-full object-cover w-40 h-40 border-4 border-white/5 md:order-2 order-1 glow glow:ring-1 glow:border-glow/[.5] glow:ring-glow shadow-inner" />
      </div>
      <div class="glow-overlay" style="--glow-color: #f43f5e"></div>
    </div>

    <!-- Fish 3 -->
    <div class="relative glow-capture">
      <div
        class="bg-zinc-900/50 border-4 border-white/5 rounded-2xl p-10 shadow-lg shadow-black/80 flex md:flex-row flex-col flex-wrap md:items-start items-center md:justify-between justify-center gap-6 backdrop-blur-md glow glow:ring-1 glow:border-glow glow:ring-glow glow:bg-glow/[.15]">
        <div class="flex-1 md:order-1 order-2">
          <p class="opacity-50">Tropical fish</p>
          <h2 class="font-bold text-4xl tracking-tighter mb-3 glow:text-glow/[.15]">Lipstick Tang</h2>
          <div class="md:prose-base prose-lg prose-zinc prose-invert text-opacity-90">
            <p>The Lipstick Tang, distinct for its striking blue body and vivid red-orange lips, resembles a vibrant
              splash
              of lipstick
              in the ocean. </p>
            <p>This eye-catching fish is a favorite among reef enthusiasts, gracefully navigating through coral
              gardens
              with elegance.</p>
          </div>
          <button
            class="font-semibold px-6 bg-zinc-950/50 backdrop-blur-md py-3 rounded-xl text-white/90 border-2 border-white/10 mt-6 glow:ring-1 glow:border-glow glow:ring-glow">Learn
            more</button>
        </div>

        <img src="fish-3.jpg" alt="Banana Wrasse"
          class="max-w-sm rounded-full object-cover w-40 h-40 border-4 border-white/5 md:order-2 order-1 glow glow:ring-1 glow:border-glow/[.5] glow:ring-glow shadow-inner" />
      </div>
      <div class="glow-overlay" style="--glow-color: #0ea5e9"></div>
    </div>

    <div class="text-sm text-white/60 text-center">
      <a href="https://webcrunch.com" target="_blank">webcrunch.com</a>
    </div>
  </div>

  <script type="module" src="/main.js"></script>
</body>

</html>
Enter fullscreen mode Exit fullscreen mode

The end result

The end result is super cool! I love this effect. We’re tricking ourselves visually with the by cloning the cards and using more modern CSS to make a neat glow effect.

One limitation I discovered is the ability to do any major transition or transform effects. Because the element is cloned it looks way off if you attempt it.

From here you can experiment with different glow colors, sizes, and transition effects to personalize the glow effect for your own needs.

I hope you found this tutorial useful. If you enjoyed it be sure to share the link with your friends.

You might also enjoy these articles

Top comments (0)