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
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
Next, we need to generate a tailwind configuration and postcss configuration file. This can be done with the following:
bunx tailwindcss init -p
Add the default Tailwind styles to your stylesheet.
/* style.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
Finally, you can boot your server running:
bun dev
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")],
}
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>
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;
}
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)",
},
},
},
}
),
],
}
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")
})
})
})
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:
- Listed first for the
DOMContentLoaded
event and then proceed - Find all elements with the
.glow-capture
class - Loop through each element with the
.glow-capture
class and clone it. - The overlay div added in our HTML gets a new child element which effectively doubles our original fish card.
- On the individual card, we listen for both the
mousemove
andmouseleave
events, setting custom CSS styles based on the mousex
andy
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>
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>
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.
Top comments (0)