Custom Cursor
Live Preview
DEMO COMMING SOON
Video Tutorial YouTube
Setup & Installation Guide
01 Add the cursor HTML
Place the two cursor divs anywhere inside your <body>. They sit in a fixed position overlay and never affect document flow.
02 Hide the native cursor
Set cursor none on html, a, and button. The pointer-events none on the custom elements means clicks still land on the elements beneath.
03 Load GSAP and initialise
Load GSAP from CDN, then paste the JS. The guard at the top skips the whole script on touch devices so mobile users keep their native pointer.
CDN
<script src="https://cdn.jsdelivr.net/npm/gsap@3/dist/gsap.min.js"></script> <!-- Cursor elements — inject once in your <body> -->
<div class="cursor cursor--dot" aria-hidden="true"></div>
<div class="cursor cursor--ring" aria-hidden="true"></div>
/* Hide default cursor on the whole page */
html { cursor: none; }
a, button { cursor: none; }
.cursor {
position: fixed;
top: 0;
left: 0;
pointer-events: none;
z-index: 9999;
border-radius: 50%;
transform: translate(-50%, -50%);
will-change: transform;
}
.cursor--dot {
width: 8px;
height: 8px;
background: #ff4d1c;
}
.cursor--ring {
width: 40px;
height: 40px;
border: 1.5px solid rgba(255, 77, 28, 0.6);
background: transparent;
transition: width 0.2s ease, height 0.2s ease, border-color 0.2s ease;
}
/* Expand ring on hoverable elements */
a:hover ~ .cursor--ring,
button:hover ~ .cursor--ring {
width: 56px;
height: 56px;
border-color: rgba(255, 77, 28, 1);
}
// Custom Cursor — GSAP quickTo
// Requires: <script src="https://cdn.jsdelivr.net/npm/gsap@3/dist/gsap.min.js"></script>
(function () {
// Skip on touch devices
if (!window.matchMedia('(pointer: fine)').matches) return;
const dot = document.querySelector('.cursor--dot');
const ring = document.querySelector('.cursor--ring');
if (!dot || !ring) return;
// Dot tracks instantly; ring lags slightly for a trailing effect
const dotX = gsap.quickTo(dot, 'x', { duration: 0.1, ease: 'none' });
const dotY = gsap.quickTo(dot, 'y', { duration: 0.1, ease: 'none' });
const ringX = gsap.quickTo(ring, 'x', { duration: 0.4, ease: 'power2.out' });
const ringY = gsap.quickTo(ring, 'y', { duration: 0.4, ease: 'power2.out' });
window.addEventListener('mousemove', (e) => {
dotX(e.clientX); dotY(e.clientY);
ringX(e.clientX); ringY(e.clientY);
});
// Scale down ring on click
window.addEventListener('mousedown', () => gsap.to(ring, { scale: 0.75, duration: 0.1 }));
window.addEventListener('mouseup', () => gsap.to(ring, { scale: 1, duration: 0.2 }));
})();
Explore More
Magnetic Button
Build a cursor-following magnetic button using GSAP quickTo. Drop in a single data attribute and the effect works on any button — no class name changes needed.
SplitText Hero Reveal
Split a hero heading into individual characters and animate them up through a line mask — no flash, no layout shift, works with custom fonts.