22 CSS Transition Effects 19 / 22

Cursor Trail Effect

Custom cursor dot and ring with four switchable trail modes: coloured dots, sparkle particles, comet streaks and rainbow — plus live position stats.

CSS + JS MIT licensed
Live Demo Open in tab

This is a full-page demo — interact inside the frame above, or open it in the playground for the full-screen experience.

Open in playground

The code

<div id="cursor-dot"></div>
<div id="cursor-ring"></div>

<div class="page">
  <h1>Cursor Trail Effect</h1>
  <p class="subtitle">Move your mouse to see different trail styles</p>

  <section>
    <h2>Trail Style</h2>
    <div class="mode-row">
      <button class="mode-btn active" data-mode="dots" onclick="setMode(this,'dots')">● Dots</button>
      <button class="mode-btn" data-mode="sparkle" onclick="setMode(this,'sparkle')">✦ Sparkle</button>
      <button class="mode-btn" data-mode="comet"   onclick="setMode(this,'comet')">— Comet</button>
      <button class="mode-btn" data-mode="rainbow" onclick="setMode(this,'rainbow')">🌈 Rainbow</button>
      <button class="mode-btn" data-mode="none"    onclick="setMode(this,'none')">✕ None</button>
    </div>
  </section>

  <section>
    <h2>Interactive Zones</h2>
    <div class="demo-zone" style="margin-bottom:16px;">
      <span class="zone-label">Hover a link</span>
      <a href="#" class="zone-link" onclick="return false;">Hover over me</a>
    </div>
    <div style="display:grid;grid-template-columns:1fr 1fr;gap:16px;">
      <div class="demo-zone">
        <span class="zone-label">Card</span>
        <div class="zone-card"><h3>Move freely</h3><p>The cursor ring expands when hovering interactive elements.</p></div>
      </div>
      <div class="demo-zone">
        <span class="zone-label">Button</span>
        <button class="neon-btn">Hover me</button>
      </div>
    </div>
  </section>

  <section>
    <h2>Stats</h2>
    <div class="info-grid">
      <div class="info-box"><div class="num" id="trailCount">0</div><p>Trail particles created</p></div>
      <div class="info-box"><div class="num" id="posX">0</div><p>Current X position</p></div>
      <div class="info-box"><div class="num" id="posY">0</div><p>Current Y position</p></div>
      <div class="info-box"><div class="num" id="speed">0</div><p>Cursor speed (px/frame)</p></div>
    </div>
  </section>
</div>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: 'Segoe UI', sans-serif; background: #060612; min-height: 100vh; overflow-x: hidden; cursor: none; }

/* ─── Custom cursor ─── */
#cursor-dot {
  position: fixed; z-index: 9999; pointer-events: none;
  width: 10px; height: 10px; border-radius: 50%;
  background: #fff; transform: translate(-50%, -50%);
  transition: width .15s, height .15s, background .15s;
  mix-blend-mode: difference;
}
#cursor-ring {
  position: fixed; z-index: 9998; pointer-events: none;
  width: 36px; height: 36px; border-radius: 50%;
  border: 1.5px solid rgba(255,255,255,.6);
  transform: translate(-50%, -50%);
  transition: transform .12s ease, width .25s, height .25s, border-color .25s;
  mix-blend-mode: difference;
}
body:has(a:hover) #cursor-ring,
body:has(button:hover) #cursor-ring { width: 56px; height: 56px; border-color: rgba(255,255,255,.9); }
body:has(a:hover) #cursor-dot,
body:has(button:hover) #cursor-dot { width: 4px; height: 4px; }

/* ─── Trails ─── */
.trail-dot {
  position: fixed; z-index: 9990; pointer-events: none; border-radius: 50%;
  transform: translate(-50%, -50%) scale(1);
  transition: opacity .6s ease, transform .6s ease;
}

/* ─── Page layout ─── */
.page { position: relative; z-index: 1; padding: 80px 40px; min-height: 100vh; }

/* Background grid */
.page::before {
  content: ''; position: fixed; inset: 0; z-index: 0;
  background-image: linear-gradient(rgba(255,255,255,.03) 1px, transparent 1px),
                    linear-gradient(90deg, rgba(255,255,255,.03) 1px, transparent 1px);
  background-size: 60px 60px;
}

h1 { color: #fff; text-align: center; font-size: 2rem; margin-bottom: 8px; position: relative; z-index: 2; }
.subtitle { color: rgba(255,255,255,.4); text-align: center; margin-bottom: 50px; position: relative; z-index: 2; }
section { margin-bottom: 60px; position: relative; z-index: 2; }
section > h2 { color: rgba(255,255,255,.35); font-size: .72rem; text-transform: uppercase; letter-spacing: 3px; margin-bottom: 20px; }

/* ─── Trail mode switcher ─── */
.mode-row { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 30px; }
.mode-btn {
  padding: 10px 20px; border-radius: 8px; border: 1px solid rgba(255,255,255,.15);
  background: rgba(255,255,255,.05); color: rgba(255,255,255,.7); font-size: .85rem; cursor: none;
  transition: background .2s, border-color .2s, color .2s;
}
.mode-btn.active { background: rgba(255,255,255,.15); border-color: rgba(255,255,255,.4); color: #fff; }

/* ─── Demo zones ─── */
.demo-zone {
  border-radius: 16px; border: 1px solid rgba(255,255,255,.08);
  background: rgba(255,255,255,.03); padding: 40px;
  display: flex; align-items: center; justify-content: center; min-height: 200px;
  position: relative; overflow: hidden;
}
.zone-label { color: rgba(255,255,255,.25); font-size: .8rem; position: absolute; top: 14px; left: 16px; text-transform: uppercase; letter-spacing: 2px; }

/* Interactive elements inside zones */
.zone-link { color: #a78bfa; text-decoration: none; font-size: 1.1rem; padding: 12px 24px; border-radius: 8px; border: 1px solid rgba(167,139,250,.3); transition: background .2s; }
.zone-link:hover { background: rgba(167,139,250,.1); }

.zone-card {
  background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.12); border-radius: 12px;
  padding: 24px; text-align: center; max-width: 260px;
  transition: transform .3s, background .3s;
}
.zone-card:hover { transform: scale(1.03); background: rgba(255,255,255,.09); }
.zone-card h3 { color: #fff; margin-bottom: 8px; }
.zone-card p  { color: rgba(255,255,255,.5); font-size: .85rem; }

.neon-btn {
  padding: 14px 36px; border-radius: 50px; border: none; cursor: none; font-size: 1rem; font-weight: 600;
  background: linear-gradient(135deg, #7c3aed, #3b82f6); color: #fff;
  box-shadow: 0 0 0 rgba(124,58,237,0); transition: box-shadow .3s, transform .2s;
}
.neon-btn:hover { box-shadow: 0 0 32px rgba(124,58,237,.6), 0 0 64px rgba(59,130,246,.3); transform: scale(1.04); }

/* Info grid */
.info-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 16px; }
.info-box {
  border-radius: 12px; padding: 20px;
  background: rgba(255,255,255,.04); border: 1px solid rgba(255,255,255,.08);
}
.info-box .num { font-size: 2rem; font-weight: 700; background: linear-gradient(135deg, #a78bfa, #60a5fa); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
.info-box p { color: rgba(255,255,255,.5); font-size: .8rem; margin-top: 4px; }
const dot  = document.getElementById('cursor-dot');
  const ring = document.getElementById('cursor-ring');
  let mx = 0, my = 0, px = 0, py = 0;
  let mode = 'dots';
  let total = 0;
  const TRAIL_COUNT = 20;

  // Move custom cursor
  document.addEventListener('mousemove', e => {
    mx = e.clientX; my = e.clientY;
    dot.style.left = mx + 'px';  dot.style.top = my + 'px';
    ring.style.left = mx + 'px'; ring.style.top = my + 'px';
    document.getElementById('posX').textContent = Math.round(mx);
    document.getElementById('posY').textContent = Math.round(my);
  });

  // Trail creation
  function createTrail(x, y, speed) {
    if (mode === 'none') return;
    const el = document.createElement('div');
    el.className = 'trail-dot';
    const size = mode === 'comet' ? Math.max(4, Math.min(speed * 2.5, 22)) : (mode === 'sparkle' ? 6 : 8);
    el.style.width  = size + 'px';
    el.style.height = size + 'px';

    if (mode === 'dots') {
      el.style.background = 'rgba(167,139,250,.8)';
    } else if (mode === 'sparkle') {
      const hue = Math.random() * 60 + 260;
      el.style.background = `hsl(${hue},90%,70%)`;
      el.style.borderRadius = Math.random() > .5 ? '0' : '50%';
      el.style.transform = `translate(-50%,-50%) rotate(${Math.random()*360}deg)`;
    } else if (mode === 'comet') {
      el.style.background = 'rgba(255,255,255,.6)';
      el.style.borderRadius = '2px';
      el.style.width = size * 3 + 'px';
    } else if (mode === 'rainbow') {
      const hue = (Date.now() / 5) % 360;
      el.style.background = `hsl(${hue},100%,65%)`;
    }

    el.style.left = x + 'px';
    el.style.top  = y + 'px';
    document.body.appendChild(el);
    total++;
    document.getElementById('trailCount').textContent = total;

    requestAnimationFrame(() => {
      el.style.opacity = '0';
      el.style.transform = `translate(-50%,-50%) scale(0)`;
    });

    setTimeout(() => el.remove(), 650);
  }

  // Throttled trail on mousemove
  let lastTrail = 0;
  document.addEventListener('mousemove', e => {
    const now = Date.now();
    const speed = Math.hypot(e.clientX - px, e.clientY - py);
    document.getElementById('speed').textContent = Math.round(speed);
    px = e.clientX; py = e.clientY;
    if (now - lastTrail > 30) {
      createTrail(e.clientX, e.clientY, speed);
      lastTrail = now;
    }
  });

  function setMode(btn, m) {
    document.querySelectorAll('.mode-btn').forEach(b => b.classList.remove('active'));
    btn.classList.add('active');
    mode = m;
  }

Search CodeFronts

Loading…