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.
This is a full-page demo — interact inside the frame above, or open it in the playground for the full-screen experience.
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> <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; } * { 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;
} 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;
}More from 22 CSS Transition Effects
Slide-In Animation on ScrollFade In Fade Out TransitionLoading Skeleton TransitionModal Open Close TransitionPage Transition EffectRipple Effect on ClickNumber Counter AnimationCard Tilt 3D HoverStaggered List AnimationMagnetic Button EffectSplit Text Reveal TransitionProgress Bar Animation
View the full collection →