Cursor Spotlight
Un voile noir à 95 % percé d'un trou de 120 px qui suit le pointeur, posé sur un contenu gardé à 15 % d'opacité au repos et remonté à 100 % au survol : le texte n'est lisible que sous le projecteur. Le curseur natif reste visible — c'est un éclairage, pas un curseur de remplacement.
Vous aurez accès à la démo interactive avec un compte gratuit.
Cet effet fait partie d'Effect.Labs — 811 effets vanilla, une partie gratuite, une partie premium. Curseurs comprend 26 effets, dont 1 gratuits. Découvrir la catégorie →
3 exemples d'usage



Comment ça marche
Trois couches dans la zone #zone-spotlight-cursor (.spotlight-cursor-zone, 300 px de haut, fond #0f0f1a, overflow: hidden). En dessous, .spotlight-content : position: absolute; inset: 0, colonne flex centrée, opacity: .15 et transition: opacity .3s. Au-dessus, #spotlightOverlay (.spotlight-cursor-overlay) : inset: 0, pointer-events: none et un radial-gradient(circle 120px at var(--spot-x, 50%) var(--spot-y, 50%), transparent 0%, rgba(0,0,0,.95) 100%). Le .info-text, déclaré après l'overlay dans le DOM, reste au-dessus du voile.
Le JS tient en un écouteur mousemove sur la zone : x = (clientX − rect.left) / rect.width × 100, idem pour y, puis spotlightOverlay.style.setProperty('--spot-x', x + '%'). Les coordonnées sont des pourcentages de la zone, donc le centre du dégradé suit le pointeur quelle que soit la taille du conteneur (mesuré : --spot-x: 25%, --spot-y: 50% pour un pointeur au quart de la largeur). Pas de requestAnimationFrame, pas de lissage : le trou est collé au pointeur, et rien ne tourne hors survol (0 appel rAF/s mesuré).
Le dégradé va de transparent au centre à rgba(0,0,0,.95) à 120 px : ce n'est pas un disque net mais un fondu sur 120 px de rayon, après quoi le voile est uniforme à 95 %. Le contenu n'est donc éclairé que dans un cercle d'environ 240 px de diamètre, et c'est le CSS seul qui le rend lisible : .spotlight-cursor-zone:hover .spotlight-content { opacity: 1 }, avec la transition de 300 ms. En sortant, :hover tombe et le contenu redescend à 15 %.
Aucun écouteur mouseleave : les variables --spot-x/--spot-y gardent leur dernière valeur (mesuré : toujours 25 %/50 % après la sortie), si bien qu'à la prochaine entrée le trou est là où on l'avait laissé jusqu'au premier mousemove. Pas de cursor: none : la flèche système reste. Deux détails dans le HTML livré : le h3 porte un style="font-family: Inter, …" en ligne (repli système si Inter n'est pas chargée) et garde ses marges navigateur (20 px mesurés, non remises à zéro).
Accessibilité
- Contraste au repos volontairement nul : le titre blanc à 15 % d'opacité sur
#0f0f1adonne 1,5:1, le paragraphe (rgba(255,255,255,.7)) 1,3:1, et sous le voile hors du cercle 1,07:1. Au survol, sous le projecteur : 19:1 et 9,5:1. Le contenu ne doit donc rien porter d'indispensable, ou être répété ailleurs ; sinon montez l'opacité de repos à.5(5,3:1 pour le blanc). - Sans survol, pas de lecture : au clavier et au tactile,
:hoverne s'installe pas de façon fiable et le contenu reste à 15 %. Ajoutez@media (hover: none) { .spotlight-content { opacity: 1 } .spotlight-cursor-overlay { display: none } }et, si la zone contient un élément focusable,.spotlight-cursor-zone:focus-within .spotlight-content { opacity: 1 }. - prefers-reduced-motion : le seul mouvement autonome est la transition d'opacité de 300 ms ; le trou suit la main, il n'est pas animé. Rien d'obligatoire, mais
@media (prefers-reduced-motion: reduce) { .spotlight-content { transition: none } }évite le fondu pour qui l'a demandé. - Lecteurs d'écran : l'overlay est un
divvide, le contenu est du DOM ordinaire annoncé normalement quelle que soit sa visibilité — c'est la bonne situation. Le.info-text(« Effet projecteur ») est lu lui aussi : retirez-le ou passez-le enaria-hiddenhors démo. - Le contenu est en
position: absolute; inset: 0dans une zone à hauteur fixe (300 px) avecoverflow: hidden: un texte plus long que la zone est coupé sans défilement. Prévoyez une hauteur enmin-heightou un contenu court ; à 200 % de zoom, vérifiez que rien ne disparaît.
Compatibilité navigateur
Requiert les propriétés personnalisées CSS avec setProperty, la propriété raccourcie inset et radial-gradient() avec taille et position explicites. JS ES2015 (fonctions fléchées, const). Zéro dépendance.
Sans JavaScript, le trou reste au centre (valeurs de repli 50 %/50 %) et le survol éclaire toujours le contenu : une vignette statique, utilisable. Sans inset (Safari avant 14.1, Firefox avant 66), contenu et overlay perdent leur taille : plus de voile, le contenu s'affiche à 15 % puis 100 % au survol.
Le code
Structure HTML à coller dans votre page (CSS + JS disponibles avec un compte premium) :
<div class="cursor-zone spotlight-cursor-zone" id="zone-spotlight-cursor">
<div class="spotlight-content">
<h3 style="font-family: Inter, -apple-system, BlinkMacSystemFont, sans-serif;">Contenu Illuminé</h3>
<p>Le curseur éclaire le contenu</p>
</div>
<div class="spotlight-cursor-overlay" id="spotlightOverlay"></div>
<span class="info-text" style="color: rgba(255,255,255,0.5);">Effet projecteur</span>
</div>
HTML + CSS + JS complet, prêt à coller — avec des centaines d'effets premium.
Personnaliser
Options passées à l'API ou attributs data-* :
| Option / propriété | Défaut | Effet |
|---|---|---|
Rayon du trou (CSS — circle 120px) |
120 px | Distance sur laquelle le voile passe de transparent à 95 %. 80 px = lampe de poche étroite ; 200 px = halo large qui dévoile un bloc entier. |
Densité du voile (CSS — rgba(0,0,0,.95)) |
.95 | Opacité du noir hors du cercle. .8 laisse deviner le contenu ; 1 le cache totalement (le pointeur est alors la seule fenêtre). |
| Opacité de repos (CSS — .spotlight-content opacity) | .15 | Visibilité du contenu sans survol. C'est le réglage d'accessibilité : .5 donne 5,3:1 sur le blanc, 1 laisse tout lisible et ne garde que le voile. |
| Transition (CSS — .spotlight-content) | opacity .3s | Durée de l'allumage et de l'extinction du contenu au survol. Le trou, lui, n'est jamais animé (variables non interpolées). |
Profil du dégradé (CSS — arrêts 0% / 100%) |
transparent 0% → noir 100% | transparent 45%, rgba(0,0,0,.95) 100% crée un disque clair franc avec un bord fondu court ; deux arrêts identiques donnent un bord net. |
| Fond de la zone (CSS — .spotlight-cursor-zone background) | #0f0f1a | Couleur vue à travers le voile et sous le trou. Un fond clair casse l'effet : gardez une teinte sombre, ou montez le voile à 1. |
Remise à zéro en sortie (JS — écouteur mouseleave à ajouter) |
absente | setProperty('--spot-x', '50%') et '--spot-y', '50%' dans un mouseleave recentrent le trou, au lieu de le laisser au dernier point survolé. |
FAQ
opacity: .15 et le voile à 95 % par-dessus. Mesuré : 1,5:1 pour le titre au repos, 1,07:1 sous le voile. Si vous voulez un contenu lisible avec un simple effet de lampe au survol, montez l'opacité de repos à 1 et gardez l'overlay : le projecteur devient un éclairage d'ambiance et le texte reste conforme.transition classique : --spot-x est une variable non typée, le navigateur ne sait pas interpoler entre deux pourcentages et saute d'une valeur à l'autre. Deux voies : enregistrer la variable avec @property --spot-x { syntax: '<percentage>'; inherits: false; initial-value: 50% } puis transition: --spot-x .15s, --spot-y .15s sur l'overlay (Chrome 85+, Safari 16.4+, Firefox 128+) ; ou lisser en JS avec une boucle requestAnimationFrame et un facteur, comme Dot Follower.mouseleave : les variables gardent leur dernière valeur (mesuré 25 %/50 % après la sortie) et le prochain survol les réutilise jusqu'au premier mouvement. Ajoutez zone.addEventListener('mouseleave', () => { overlay.style.setProperty('--spot-x', '50%'); overlay.style.setProperty('--spot-y', '50%'); }). Le contenu, lui, redescend bien à 15 % puisque :hover est géré par le CSS.