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.

GSAP Interactions Animation

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.

GSAP Animation SplitText