UI Components Medium
Lens Effect
Magnifying glass lens effect that follows the cursor, showing a zoomed circular area over content with smooth tracking and customizable zoom level.
Open in Lab
MCP
css javascript vue svelte
Targets: TS JS HTML React Vue Svelte
Code
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: system-ui, -apple-system, sans-serif;
min-height: 100vh;
background: #0a0a0a;
overflow: hidden;
display: grid;
place-items: center;
}
.lens-wrapper {
position: relative;
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 2rem;
}
.lens-area {
position: relative;
width: min(600px, 90vw);
height: min(400px, 60vh);
border-radius: 16px;
overflow: hidden;
cursor: none;
border: 1px solid rgba(255, 255, 255, 0.08);
}
.lens-content {
position: relative;
width: 100%;
height: 100%;
background: #111;
}
.grid-pattern {
position: absolute;
inset: 0;
background-image: linear-gradient(rgba(139, 92, 246, 0.08) 1px, transparent 1px),
linear-gradient(90deg, rgba(139, 92, 246, 0.08) 1px, transparent 1px);
background-size: 20px 20px;
}
.content-overlay {
position: relative;
z-index: 2;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 2rem;
text-align: center;
}
.content-title {
font-size: 1.5rem;
font-weight: 700;
color: #e2e8f0;
margin-bottom: 0.75rem;
}
.content-text {
font-size: 0.875rem;
color: rgba(148, 163, 184, 0.7);
max-width: 400px;
line-height: 1.6;
margin-bottom: 1.5rem;
}
.detail-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
}
.detail-cell {
width: 48px;
height: 48px;
border-radius: 8px;
background: hsl(var(--hue, 260) 60% 50% / 0.3);
border: 1px solid hsl(var(--hue, 260) 60% 60% / 0.2);
position: relative;
}
.detail-cell::after {
content: "";
position: absolute;
inset: 4px;
border-radius: 4px;
background: hsl(var(--hue, 260) 70% 60% / 0.15);
}
.lens {
position: absolute;
width: 160px;
height: 160px;
border-radius: 50%;
border: 2px solid rgba(139, 92, 246, 0.5);
box-shadow: 0 0 30px rgba(139, 92, 246, 0.2), inset 0 0 20px rgba(139, 92, 246, 0.05);
pointer-events: none;
opacity: 0;
transition: opacity 0.2s ease;
overflow: hidden;
z-index: 100;
transform: translate(-50%, -50%);
background: #111;
}
.lens.active {
opacity: 1;
}
.lens::after {
content: "";
position: absolute;
inset: 0;
border-radius: 50%;
background: radial-gradient(circle at 35% 35%, rgba(255, 255, 255, 0.1) 0%, transparent 50%);
pointer-events: none;
}
.page-title {
text-align: center;
pointer-events: none;
}
.title {
font-size: clamp(2rem, 5vw, 3.5rem);
font-weight: 800;
letter-spacing: -0.03em;
background: linear-gradient(135deg, #c4b5fd 0%, #8b5cf6 50%, #7c3aed 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 0.5rem;
}
.subtitle {
font-size: clamp(0.875rem, 2vw, 1.125rem);
color: rgba(148, 163, 184, 0.8);
font-weight: 400;
}// Lens Effect — magnifying glass that follows cursor
(function () {
"use strict";
const area = document.getElementById("lens-area");
const lens = document.getElementById("lens");
if (!area || !lens) return;
const ZOOM = 2.5;
const LENS_SIZE = 160;
const LERP_FACTOR = 0.15;
let mouseX = 0;
let mouseY = 0;
let lensX = 0;
let lensY = 0;
let isActive = false;
let animId = null;
// Capture the content area for the lens background
function updateLensBackground() {
const content = area.querySelector(".lens-content");
if (!content) return;
// Use the area's own rendering as the lens source via element cloning
const clone = content.cloneNode(true);
clone.style.position = "absolute";
clone.style.width = area.clientWidth + "px";
clone.style.height = area.clientHeight + "px";
clone.style.transform = `scale(${ZOOM})`;
clone.style.transformOrigin = "0 0";
clone.style.pointerEvents = "none";
clone.style.top = "0";
clone.style.left = "0";
clone.id = "";
// Clear old clone
const existing = lens.querySelector(".lens-clone");
if (existing) existing.remove();
clone.classList.add("lens-clone");
lens.appendChild(clone);
}
updateLensBackground();
function updateLensPosition() {
const rect = area.getBoundingClientRect();
const relX = mouseX - rect.left;
const relY = mouseY - rect.top;
// Lerp for smooth following
lensX += (relX - lensX) * LERP_FACTOR;
lensY += (relY - lensY) * LERP_FACTOR;
lens.style.left = lensX + "px";
lens.style.top = lensY + "px";
// Move the cloned content inside the lens
const clone = lens.querySelector(".lens-clone");
if (clone) {
const offsetX = -(lensX * ZOOM - LENS_SIZE / 2);
const offsetY = -(lensY * ZOOM - LENS_SIZE / 2);
clone.style.transform = `scale(${ZOOM})`;
clone.style.left = offsetX + "px";
clone.style.top = offsetY + "px";
}
if (isActive) {
animId = requestAnimationFrame(updateLensPosition);
}
}
area.addEventListener("mouseenter", () => {
isActive = true;
lens.classList.add("active");
updateLensBackground();
animId = requestAnimationFrame(updateLensPosition);
});
area.addEventListener("mousemove", (e) => {
mouseX = e.clientX;
mouseY = e.clientY;
});
area.addEventListener("mouseleave", () => {
isActive = false;
lens.classList.remove("active");
if (animId) {
cancelAnimationFrame(animId);
animId = null;
}
});
// Update on resize
window.addEventListener("resize", () => {
updateLensBackground();
});
})();<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Lens Effect</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="lens-wrapper">
<div id="lens-area" class="lens-area">
<div class="lens-content">
<div class="grid-pattern"></div>
<div class="content-overlay">
<h2 class="content-title">Hover to Magnify</h2>
<p class="content-text">Move your cursor over this area to see the lens effect in action. The magnifying glass reveals hidden detail in the grid pattern below.</p>
<div class="detail-grid">
<div class="detail-cell" style="--hue: 260"></div>
<div class="detail-cell" style="--hue: 280"></div>
<div class="detail-cell" style="--hue: 300"></div>
<div class="detail-cell" style="--hue: 320"></div>
<div class="detail-cell" style="--hue: 200"></div>
<div class="detail-cell" style="--hue: 220"></div>
<div class="detail-cell" style="--hue: 240"></div>
<div class="detail-cell" style="--hue: 180"></div>
<div class="detail-cell" style="--hue: 160"></div>
</div>
</div>
</div>
<div id="lens" class="lens"></div>
</div>
<div class="page-title">
<h1 class="title">Lens Effect</h1>
<p class="subtitle">Magnifying glass that follows your cursor</p>
</div>
</div>
<script src="script.js"></script>
</body>
</html>import { useRef, useEffect, useCallback, useState, type ReactNode } from "react";
interface LensEffectProps {
zoom?: number;
lensSize?: number;
lerpFactor?: number;
borderColor?: string;
children?: ReactNode;
className?: string;
}
export function LensEffect({
zoom = 2.5,
lensSize = 160,
lerpFactor = 0.15,
borderColor = "rgba(139, 92, 246, 0.5)",
children,
className = "",
}: LensEffectProps) {
const areaRef = useRef<HTMLDivElement>(null);
const lensRef = useRef<HTMLDivElement>(null);
const contentRef = useRef<HTMLDivElement>(null);
const cloneRef = useRef<HTMLDivElement>(null);
const mouseRef = useRef({ x: 0, y: 0 });
const posRef = useRef({ x: 0, y: 0 });
const activeRef = useRef(false);
const animRef = useRef<number | null>(null);
const updateClone = useCallback(() => {
const area = areaRef.current;
const lens = lensRef.current;
const content = contentRef.current;
if (!area || !lens || !content) return;
if (cloneRef.current) cloneRef.current.remove();
const clone = content.cloneNode(true) as HTMLDivElement;
clone.style.position = "absolute";
clone.style.width = area.clientWidth + "px";
clone.style.height = area.clientHeight + "px";
clone.style.transform = `scale(${zoom})`;
clone.style.transformOrigin = "0 0";
clone.style.pointerEvents = "none";
clone.style.top = "0";
clone.style.left = "0";
lens.appendChild(clone);
cloneRef.current = clone;
}, [zoom]);
const tick = useCallback(() => {
const area = areaRef.current;
const lens = lensRef.current;
const clone = cloneRef.current;
if (!area || !lens) return;
const rect = area.getBoundingClientRect();
const relX = mouseRef.current.x - rect.left;
const relY = mouseRef.current.y - rect.top;
posRef.current.x += (relX - posRef.current.x) * lerpFactor;
posRef.current.y += (relY - posRef.current.y) * lerpFactor;
lens.style.left = posRef.current.x + "px";
lens.style.top = posRef.current.y + "px";
if (clone) {
clone.style.transform = `scale(${zoom})`;
clone.style.left = -(posRef.current.x * zoom - lensSize / 2) + "px";
clone.style.top = -(posRef.current.y * zoom - lensSize / 2) + "px";
}
if (activeRef.current) {
animRef.current = requestAnimationFrame(tick);
}
}, [zoom, lensSize, lerpFactor]);
const [active, setActive] = useState(false);
return (
<div
ref={areaRef}
className={className}
style={{
position: "relative",
overflow: "hidden",
cursor: "none",
borderRadius: 16,
border: "1px solid rgba(255,255,255,0.08)",
}}
onMouseEnter={() => {
activeRef.current = true;
setActive(true);
updateClone();
animRef.current = requestAnimationFrame(tick);
}}
onMouseMove={(e) => {
mouseRef.current = { x: e.clientX, y: e.clientY };
}}
onMouseLeave={() => {
activeRef.current = false;
setActive(false);
if (animRef.current) cancelAnimationFrame(animRef.current);
}}
>
<div ref={contentRef} style={{ position: "relative", width: "100%", height: "100%" }}>
{children}
</div>
<div
ref={lensRef}
style={{
position: "absolute",
width: lensSize,
height: lensSize,
borderRadius: "50%",
border: `2px solid ${borderColor}`,
boxShadow: `0 0 30px ${borderColor.replace(/[\d.]+\)$/, "0.2)")}, inset 0 0 20px ${borderColor.replace(/[\d.]+\)$/, "0.05)")}`,
pointerEvents: "none",
opacity: active ? 1 : 0,
transition: "opacity 0.2s ease",
overflow: "hidden",
zIndex: 100,
transform: "translate(-50%, -50%)",
background: "#111",
}}
>
<div
style={{
position: "absolute",
inset: 0,
borderRadius: "50%",
background:
"radial-gradient(circle at 35% 35%, rgba(255,255,255,0.1) 0%, transparent 50%)",
pointerEvents: "none",
zIndex: 10,
}}
/>
</div>
</div>
);
}
// Demo usage
export default function LensEffectDemo() {
return (
<div
style={{
width: "100vw",
height: "100vh",
background: "#0a0a0a",
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
gap: "2rem",
fontFamily: "system-ui, -apple-system, sans-serif",
}}
>
<LensEffect zoom={2.5} lensSize={160}>
<div
style={{
width: "min(600px, 90vw)",
height: "min(400px, 60vh)",
background: "#111",
position: "relative",
}}
>
{/* Grid pattern */}
<div
style={{
position: "absolute",
inset: 0,
backgroundImage:
"linear-gradient(rgba(139,92,246,0.08) 1px, transparent 1px), linear-gradient(90deg, rgba(139,92,246,0.08) 1px, transparent 1px)",
backgroundSize: "20px 20px",
}}
/>
<div
style={{
position: "relative",
zIndex: 2,
height: "100%",
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
padding: "2rem",
textAlign: "center",
}}
>
<h2
style={{
fontSize: "1.5rem",
fontWeight: 700,
color: "#e2e8f0",
marginBottom: "0.75rem",
}}
>
Hover to Magnify
</h2>
<p
style={{
fontSize: "0.875rem",
color: "rgba(148,163,184,0.7)",
maxWidth: 400,
lineHeight: 1.6,
marginBottom: "1.5rem",
}}
>
Move your cursor over this area to see the lens effect in action.
</p>
<div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 8 }}>
{[260, 280, 300, 320, 200, 220, 240, 180, 160].map((hue, i) => (
<div
key={i}
style={{
width: 48,
height: 48,
borderRadius: 8,
background: `hsl(${hue} 60% 50% / 0.3)`,
border: `1px solid hsl(${hue} 60% 60% / 0.2)`,
}}
/>
))}
</div>
</div>
</div>
</LensEffect>
<div style={{ textAlign: "center", pointerEvents: "none" }}>
<h1
style={{
fontSize: "clamp(2rem, 5vw, 3.5rem)",
fontWeight: 800,
letterSpacing: "-0.03em",
background: "linear-gradient(135deg, #c4b5fd 0%, #8b5cf6 50%, #7c3aed 100%)",
WebkitBackgroundClip: "text",
WebkitTextFillColor: "transparent",
backgroundClip: "text",
marginBottom: "0.5rem",
}}
>
Lens Effect
</h1>
<p style={{ fontSize: "clamp(0.875rem, 2vw, 1.125rem)", color: "rgba(148,163,184,0.8)" }}>
Magnifying glass that follows your cursor
</p>
</div>
</div>
);
}<script setup>
import { ref, onMounted, onUnmounted } from "vue";
const zoom = 2.5;
const lensSize = 160;
const lerpFactor = 0.15;
const borderColor = "rgba(139, 92, 246, 0.5)";
const areaEl = ref(null);
const lensEl = ref(null);
const contentEl = ref(null);
const active = ref(false);
let cloneNode = null;
let mouse = { x: 0, y: 0 };
let pos = { x: 0, y: 0 };
let isActive = false;
let animId = null;
function updateClone() {
const area = areaEl.value;
const lens = lensEl.value;
const content = contentEl.value;
if (!area || !lens || !content) return;
if (cloneNode) cloneNode.remove();
const clone = content.cloneNode(true);
clone.style.position = "absolute";
clone.style.width = area.clientWidth + "px";
clone.style.height = area.clientHeight + "px";
clone.style.transform = `scale(${zoom})`;
clone.style.transformOrigin = "0 0";
clone.style.pointerEvents = "none";
clone.style.top = "0";
clone.style.left = "0";
lens.appendChild(clone);
cloneNode = clone;
}
function tickLoop() {
const area = areaEl.value;
const lens = lensEl.value;
if (!area || !lens) return;
const rect = area.getBoundingClientRect();
const relX = mouse.x - rect.left;
const relY = mouse.y - rect.top;
pos.x += (relX - pos.x) * lerpFactor;
pos.y += (relY - pos.y) * lerpFactor;
lens.style.left = pos.x + "px";
lens.style.top = pos.y + "px";
if (cloneNode) {
cloneNode.style.transform = `scale(${zoom})`;
cloneNode.style.left = -(pos.x * zoom - lensSize / 2) + "px";
cloneNode.style.top = -(pos.y * zoom - lensSize / 2) + "px";
}
if (isActive) {
animId = requestAnimationFrame(tickLoop);
}
}
function onEnter() {
isActive = true;
active.value = true;
updateClone();
animId = requestAnimationFrame(tickLoop);
}
function onMove(e) {
mouse = { x: e.clientX, y: e.clientY };
}
function onLeave() {
isActive = false;
active.value = false;
if (animId) cancelAnimationFrame(animId);
}
const hues = [260, 280, 300, 320, 200, 220, 240, 180, 160];
onUnmounted(() => {
if (animId) cancelAnimationFrame(animId);
});
</script>
<template>
<div style="width:100vw;height:100vh;background:#0a0a0a;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2rem;font-family:system-ui,-apple-system,sans-serif">
<div
ref="areaEl"
@mouseenter="onEnter"
@mousemove="onMove"
@mouseleave="onLeave"
style="position:relative;overflow:hidden;cursor:none;border-radius:16px;border:1px solid rgba(255,255,255,0.08)"
>
<div ref="contentEl" style="position:relative;width:100%;height:100%">
<div style="width:min(600px,90vw);height:min(400px,60vh);background:#111;position:relative">
<div style="position:absolute;inset:0;background-image:linear-gradient(rgba(139,92,246,0.08) 1px, transparent 1px), linear-gradient(90deg, rgba(139,92,246,0.08) 1px, transparent 1px);background-size:20px 20px"></div>
<div style="position:relative;z-index:2;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:2rem;text-align:center">
<h2 style="font-size:1.5rem;font-weight:700;color:#e2e8f0;margin-bottom:0.75rem">Hover to Magnify</h2>
<p style="font-size:0.875rem;color:rgba(148,163,184,0.7);max-width:400px;line-height:1.6;margin-bottom:1.5rem">
Move your cursor over this area to see the lens effect in action.
</p>
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:8px">
<div
v-for="(hue, i) in hues"
:key="i"
:style="{
width: '48px',
height: '48px',
borderRadius: '8px',
background: `hsl(${hue} 60% 50% / 0.3)`,
border: `1px solid hsl(${hue} 60% 60% / 0.2)`,
}"
/>
</div>
</div>
</div>
</div>
<div
ref="lensEl"
:style="{
position: 'absolute',
width: lensSize + 'px',
height: lensSize + 'px',
borderRadius: '50%',
border: `2px solid ${borderColor}`,
boxShadow: `0 0 30px rgba(139,92,246,0.2), inset 0 0 20px rgba(139,92,246,0.05)`,
pointerEvents: 'none',
opacity: active ? 1 : 0,
transition: 'opacity 0.2s ease',
overflow: 'hidden',
zIndex: 100,
transform: 'translate(-50%, -50%)',
background: '#111',
}"
>
<div style="position:absolute;inset:0;border-radius:50%;background:radial-gradient(circle at 35% 35%, rgba(255,255,255,0.1) 0%, transparent 50%);pointer-events:none;z-index:10"></div>
</div>
</div>
<div style="text-align:center;pointer-events:none">
<h1 style="font-size:clamp(2rem,5vw,3.5rem);font-weight:800;letter-spacing:-0.03em;background:linear-gradient(135deg,#c4b5fd 0%,#8b5cf6 50%,#7c3aed 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;margin-bottom:0.5rem">
Lens Effect
</h1>
<p style="font-size:clamp(0.875rem,2vw,1.125rem);color:rgba(148,163,184,0.8)">
Magnifying glass that follows your cursor
</p>
</div>
</div>
</template><script>
import { onDestroy } from "svelte";
const zoom = 2.5;
const lensSize = 160;
const lerpFactor = 0.15;
const borderColor = "rgba(139, 92, 246, 0.5)";
let areaEl;
let lensEl;
let contentEl;
let active = false;
let cloneNode = null;
let mouse = { x: 0, y: 0 };
let pos = { x: 0, y: 0 };
let isActive = false;
let animId = null;
function updateClone() {
if (!areaEl || !lensEl || !contentEl) return;
if (cloneNode) cloneNode.remove();
const clone = contentEl.cloneNode(true);
clone.style.position = "absolute";
clone.style.width = areaEl.clientWidth + "px";
clone.style.height = areaEl.clientHeight + "px";
clone.style.transform = `scale(${zoom})`;
clone.style.transformOrigin = "0 0";
clone.style.pointerEvents = "none";
clone.style.top = "0";
clone.style.left = "0";
lensEl.appendChild(clone);
cloneNode = clone;
}
function tickLoop() {
if (!areaEl || !lensEl) return;
const rect = areaEl.getBoundingClientRect();
const relX = mouse.x - rect.left;
const relY = mouse.y - rect.top;
pos.x += (relX - pos.x) * lerpFactor;
pos.y += (relY - pos.y) * lerpFactor;
lensEl.style.left = pos.x + "px";
lensEl.style.top = pos.y + "px";
if (cloneNode) {
cloneNode.style.transform = `scale(${zoom})`;
cloneNode.style.left = -(pos.x * zoom - lensSize / 2) + "px";
cloneNode.style.top = -(pos.y * zoom - lensSize / 2) + "px";
}
if (isActive) {
animId = requestAnimationFrame(tickLoop);
}
}
function onEnter() {
isActive = true;
active = true;
updateClone();
animId = requestAnimationFrame(tickLoop);
}
function onMove(e) {
mouse = { x: e.clientX, y: e.clientY };
}
function onLeave() {
isActive = false;
active = false;
if (animId) cancelAnimationFrame(animId);
}
const hues = [260, 280, 300, 320, 200, 220, 240, 180, 160];
onDestroy(() => {
if (animId) cancelAnimationFrame(animId);
});
</script>
<div style="width:100vw;height:100vh;background:#0a0a0a;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2rem;font-family:system-ui,-apple-system,sans-serif">
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
bind:this={areaEl}
on:mouseenter={onEnter}
on:mousemove={onMove}
on:mouseleave={onLeave}
style="position:relative;overflow:hidden;cursor:none;border-radius:16px;border:1px solid rgba(255,255,255,0.08)"
>
<div bind:this={contentEl} style="position:relative;width:100%;height:100%">
<div style="width:min(600px,90vw);height:min(400px,60vh);background:#111;position:relative">
<div style="position:absolute;inset:0;background-image:linear-gradient(rgba(139,92,246,0.08) 1px, transparent 1px), linear-gradient(90deg, rgba(139,92,246,0.08) 1px, transparent 1px);background-size:20px 20px"></div>
<div style="position:relative;z-index:2;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:2rem;text-align:center">
<h2 style="font-size:1.5rem;font-weight:700;color:#e2e8f0;margin-bottom:0.75rem">Hover to Magnify</h2>
<p style="font-size:0.875rem;color:rgba(148,163,184,0.7);max-width:400px;line-height:1.6;margin-bottom:1.5rem">
Move your cursor over this area to see the lens effect in action.
</p>
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:8px">
{#each hues as hue, i}
<div style="width:48px;height:48px;border-radius:8px;background:hsl({hue} 60% 50% / 0.3);border:1px solid hsl({hue} 60% 60% / 0.2)"></div>
{/each}
</div>
</div>
</div>
</div>
<div
bind:this={lensEl}
style="position:absolute;width:{lensSize}px;height:{lensSize}px;border-radius:50%;border:2px solid {borderColor};box-shadow:0 0 30px rgba(139,92,246,0.2), inset 0 0 20px rgba(139,92,246,0.05);pointer-events:none;opacity:{active ? 1 : 0};transition:opacity 0.2s ease;overflow:hidden;z-index:100;transform:translate(-50%,-50%);background:#111"
>
<div style="position:absolute;inset:0;border-radius:50%;background:radial-gradient(circle at 35% 35%, rgba(255,255,255,0.1) 0%, transparent 50%);pointer-events:none;z-index:10"></div>
</div>
</div>
<div style="text-align:center;pointer-events:none">
<h1 style="font-size:clamp(2rem,5vw,3.5rem);font-weight:800;letter-spacing:-0.03em;background:linear-gradient(135deg,#c4b5fd 0%,#8b5cf6 50%,#7c3aed 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;margin-bottom:0.5rem">
Lens Effect
</h1>
<p style="font-size:clamp(0.875rem,2vw,1.125rem);color:rgba(148,163,184,0.8)">
Magnifying glass that follows your cursor
</p>
</div>
</div>Lens Effect
A magnifying glass effect that tracks the cursor and shows a zoomed circular view of the content underneath. The lens follows mouse movement smoothly with a subtle spring animation.
How it works
- A circular lens element is absolutely positioned and follows the mouse
- The lens contains a scaled clone of the background content via
background-image+background-size - Background position is offset based on cursor position to keep the zoom centered
- Smooth tracking uses lerp (linear interpolation) for a polished feel
- The lens hides when the cursor leaves the content area
Customization
ZOOM_LEVELcontrols magnification (default 2x)LENS_SIZEsets the diameter of the magnifying circle- Border, shadow, and chromatic aberration effects can be toggled
- Works with images, text, or any HTML content
When to use it
- Image galleries with detail zoom
- Product image inspection
- Map detail views
- Interactive art / photography portfolios