Cursor Spotlight
Un voile à 97 % d'opacité couvre toute la zone ; à chaque mousemove, le JS calcule les coordonnées pixel du curseur et les réécrit dans le background de l'overlay — seul un cercle de 90 px reste transparent.
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
L'effet repose sur un élément .spotlight-overlay positionné en inset: 0 au-dessus du contenu. En CSS, cet overlay démarre avec background: radial-gradient(circle 80px at var(--mouse-x, 50%) var(--mouse-y, 50%), transparent 0%, rgba(10,10,26,0.97) 100%). Le chiffre 0.97 est la clé : à 97 % d'opacité, le fond est presque noir et seul le centre du dégradé laisse passer la lumière. Au repos, ce trou est centré à 50% 50% — une petite fenêtre reste ouverte sans interaction, mais tout le reste du contenu est masqué.
L'écouteur mousemove est posé sur .alchemy-spotlight-zone, le conteneur qui porte le contenu caché et l'overlay. À chaque événement, getBoundingClientRect() lit la position du conteneur ; le script soustrait l'origine pour obtenir des coordonnées en pixels absolus : r = a.clientX - n.left et l = a.clientY - n.top. Ces valeurs sont injectées dans t.style.background : radial-gradient(circle 90px at ${r}px ${l}px, transparent 0%, rgba(10,10,26,0.97) 100%). Le cercle passe à 90 px pendant le mouvement — légèrement plus large qu'au repos — et le JS réécrit l'intégralité du background inline à chaque événement, sans variable CSS intermédiaire.
Un second écouteur mouseleave remet le style.background à radial-gradient(circle 80px at 50% 50%, transparent 0%, rgba(10,10,26,0.97) 100%) : l'ouverture revient brusquement au centre, sans transition. Ce retour sec accentue le sentiment de rideau tiré — c'est un choix délibéré. L'activation passe par querySelectorAll('[data-spotlight]') : toutes les zones portant cet attribut sont équipées en une seule boucle, chaque overlay réagissant exclusivement aux événements de sa propre zone.
Accessibilité
- prefers-reduced-motion : absent du code. Le dégradé est réécrit à chaque
mousemovesans égard pour la préférence système. Conditionne l'enregistrement des écouteurs àwindow.matchMedia('(prefers-reduced-motion: reduce)'), mais laisse alors une ouverture fixe visible — sinon le contenu caché devient inaccessible en mode mouvement réduit. - L'overlay est à
pointer-events: none: les liens et boutons de.spotlight-hidden-contentrestent cliquables et atteignables au Tab. Le curseur est masqué viacursor: nonesur la zone — le Tab passe sans problème, mais sans indicateur de focus visible. Définis un état:focus-visibleexplicite sur chaque élément interactif caché. - En dehors du cercle de 90 px, la couche à 97 % d'opacité ramène le contraste à presque zéro — intentionnellement. Si le contenu n'est pas purement décoratif, prévois un bouton « Révéler » ou une version visible par défaut activée uniquement quand JavaScript et un pointeur fin sont disponibles.
- Sur mobile et tablette, aucun
mousemovene circule : l'overlay reste figé et le trou de 80 px est centré. Tout le contenu hors de ce cercle est invisible et inaccessible au toucher. Réserve cet effet à du contenu décoratif, ou expose le contenu par défaut et conditionne l'overlay à(hover: hover) and (pointer: fine).
Compatibilité navigateur
Le code utilise querySelectorAll, des fonctions fléchées et des template literals — ES2015. IE 11 lève une erreur de syntaxe avant la première exécution. Côté CSS, radial-gradient(circle Xpx at Ypx Zpx, …) est pris en charge sans préfixe dans tous les navigateurs actuels.
Sans JavaScript, l'overlay conserve son <code>background</code> CSS initial : un cercle de 80 px centré à <code>50% 50%</code>. Le contenu hors de ce cercle reste masqué — pas un écran noir, mais non découvrable. Pour un repli propre, scope l'overlay à une classe <code>js-ready</code> ajoutée au chargement : sans JS, pas d'overlay.
Le code
Structure HTML à coller dans votre page (CSS + JS disponibles avec un compte premium) :
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 cercle pendant le mouvement (90 dans le JS) | 90 px | La valeur 90px est dans le template literal du mousemove. Augmente pour un faisceau large, réduis pour une torche étroite. Ne confonds pas avec les 80 px du CSS — pendant le mouvement, c'est 90 qui s'applique. |
| Rayon du cercle au repos (80 dans le CSS et dans mouseleave) | 80 px | Ce chiffre apparaît dans le background CSS initial et dans le handler mouseleave. Modifie-les ensemble : en les changeant séparément, le cercle saute d'une taille à l'autre au retour du curseur — un à-coup visible. |
| Opacité de l'overlay (0.97 dans rgba) | 0.97 | À 0.97, le fond est presque noir. Descends à 0.80 pour un brouillard léger ; monte à 1.0 pour un masque totalement opaque. Mets à jour les trois occurrences — CSS initial, mousemove et mouseleave. |
| Couleur de l'overlay (10, 10, 26 dans rgba) | rgba(10, 10, 26, …) | Le triplet 10, 10, 26 produit un bleu-nuit presque noir. Pour un fond blanc, passe à rgba(255,255,255,…). Change les trois occurrences (CSS + deux endroits dans le JS) pour éviter un raccord visible entre le rideau et la page. |
| Attribut de déclenchement (data-spotlight) | [data-spotlight] | querySelectorAll('[data-spotlight]') équipe toutes les zones portant l'attribut, sans limite. Pour des paramètres différents par zone, lis el.dataset.spotlightRadius dans la boucle et utilise la valeur dans le template literal — la configuration se fait côté HTML. |
| Curseur masqué (cursor: none sur .alchemy-spotlight-zone) | curseur natif masqué | Masquer le curseur renforce l'illusion que le faisceau est le pointeur. Retire cursor: none si tu veux conserver le curseur natif — l'effet fonctionne, mais on voit alors deux cercles se déplacer ensemble (le pointeur + le halo), ce qui réduit l'immersion. |
FAQ
mask: radial-gradient) — le contenu n'est visible que là où le masque est opaque — et le JS écrit les coordonnées en variables CSS pourcentage (--x: 45%). Cursor Spotlight fait l'inverse : un overlay opaque recouvre tout et le JS perce un trou en réécrivant le background en pixels absolus (circle 90px at 312px 87px) à chaque mousemove, sans variable intermédiaire. En pratique, Spotlight Reveal révèle du texte coloré ; Cursor Spotlight masque n'importe quel contenu derrière un vrai rideau et s'active sur autant de zones que nécessaire via data-spotlight.data-spotlight-radius="120", puis lis parseInt(el.dataset.spotlightRadius, 10) || 90 dans la boucle querySelectorAll et utilise cette variable dans le template literal du mousemove. La boucle existante reste intacte..spotlight-hidden-content reste dans le flux normal, lisible par les moteurs de recherche, et cliquable si pointer-events: none est bien posé sur l'overlay. Ce n'est pas une technique de dissimulation SEO — les robots voient le texte. Si tu veux vraiment masquer du contenu aux moteurs, charge-le via JavaScript après une action explicite de l'utilisateur.