Glow Effect
Un orbe lumineux distinct suit le pointeur dans la zone : `mouseenter` l'affiche via `display: block`, chaque `mousemove` le repositionne avec un décalage fixe de 40 px sur chaque axe, `mouseleave` le masque — et la zone reçoit la classe `cursor-active` pour que le CSS puisse effacer le curseur natif.
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 sépare strictement périmètre et curseur. #zone-glow définit la scène : fond sombre, border-radius d'un rem, et surtout overflow: hidden — le délimiteur silencieux qui rogne l'orbe à ses bords sans JS supplémentaire. #cursorGlow est un élément distinct du DOM, hors du flux, display: none par défaut. C'est lui seul qui se déplace ; la zone ne bouge jamais. Ce partage des rôles distingue l'effet d'un fond en dégradé radial : pas de couche recalculée à chaque frame, un élément positionné qui suit le pointeur.
Trois écouteurs, trois rôles stricts. mouseenter exécute deux instructions : ajout de cursor-active sur #zone-glow (point d'accroche CSS pour cursor: none) puis #cursorGlow à display: block — apparition instantanée, sans fondu. mouseleave inverse la paire dans le même ordre. Entre les deux, mousemove réécrit la position à chaque événement : cursorGlow.style.left = e.clientX - 40 + 'px' et cursorGlow.style.top = e.clientY - 40 + 'px'. Deux affectations de style inline par mouvement — pas de requestAnimationFrame, pas de getBoundingClientRect(). La mise à jour est synchrone avec chaque déplacement.
clientX et clientY mesurent la position dans le viewport, pas dans la zone. Soustraire 40 px sur chaque axe centre un orbe de 80 × 80 px sur le curseur : le décalage vaut la moitié des dimensions, et #cursorGlow doit être en position: fixed pour que left: clientX − 40px soit valide quel que soit le défilement. overflow: hidden rogne l'orbe visuellement à ses bords sans qu'il soit un enfant CSS de la zone. Si tu changes la taille de l'orbe, recalcule le décalage à largeur / 2 et hauteur / 2.
Accessibilité
- prefers-reduced-motion : aucune
transitionni@keyframesdans le snippet. L'usage réel de#cursorGlowimplique pourtant presque toujours unfilter: blur()ou une pulsation — conditionne-les derrière@media (prefers-reduced-motion: reduce). Le repositionnement parleft/topn'est pas une animation CSS et ignore les media queries de mouvement : pour les visiteurs sensibles, désactive l'écouteurmousemovequand la préférence est active. - L'orbe reste en
display: nonesur écran tactile : aucunmouseenternimousemovene se déclenche en usage normal. Attention si tu appliquescursor: nonedans.cursor-active: certains navigateurs hybrides envoient un événement de survol synthétique au premier tap. Conditionne l'ajout decursor-activeàmatchMedia('(hover: hover) and (pointer: fine)')pour les environnements hybrides. - Aucun des trois écouteurs ne répond au clavier : Tab, Enter, Space fonctionnent normalement. Si tu utilises
cursor: nonedans.cursor-active, assure-toi que la règle disparaît dès que le pointeur quitte la zone. Définis un:focus-visiblemarqué sur les éléments interactifs — l'orbe glow n'est pas un substitut pour la navigation clavier. #cursorGlowest purement décoratif — pas de texte, pas de rôle. Posearia-hidden="true"dessus : undivpassant dedisplay: noneàdisplay: blockpeut être annoncé par certains outils d'assistance sans cet attribut. Ne porte aucune information par le glow — il est invisible pour une partie non négligeable de tes visiteurs.
Compatibilité navigateur
L'effet n'utilise que du DOM de base : getElementById, classList.add/remove, et des affectations de style.left/style.top/style.display. Pas de custom properties. Le seul plancher est syntaxique : les fonctions fléchées requièrent ES2015 — IE 11 échoue avant le premier mouseenter.
Sans JavaScript, <code>#cursorGlow</code> reste en <code>display: none</code> : la zone s'affiche normalement, le curseur natif reste visible. Sur un moteur ES5, l'erreur de syntaxe stoppe le script avant toute attache d'écouteur — même repli propre. L'effet est purement additif.
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 |
|---|---|---|
| Décalage de centrage (40 dans le JS) | 40 px — pour un orbe de 80 × 80 px | La valeur 40 apparaît deux fois : e.clientX - 40 et e.clientY - 40. Elle doit valoir la moitié des dimensions. Un orbe 120 × 60 px exige des décalages dissymétriques (60 et 30). Un décalage incorrect décentre le halo et révèle la mécanique. |
| Bascule d'affichage (display: none → block) | display: none / display: block | display est binaire et ne peut pas être transitionné. Pour un fondu, remplace par opacity + visibility avec transition: opacity .2s : visibility: hidden retire l'orbe des événements pointeur pendant l'invisibilité, évitant les mousemove parasites si la zone et l'orbe se chevauchent. |
| Classe cursor-active sur #zone-glow | classe sans style imposé par le JS | Le JS ajoute et retire cursor-active en miroir de la visibilité de l'orbe ; ton CSS décide quoi en faire. Usage principal : #zone-glow.cursor-active { cursor: none } pour masquer le pointeur natif. Secondairement : changer la border-color ou déclencher une animation CSS sur le fond. |
| Identifiant de l'orbe (#cursorGlow) | getElementById('cursorGlow') | L'orbe est cherché par id au démarrage ; absent, le script s'arrête proprement (if (!cursorGlow) return). Mets l'id à jour dans le JS et le HTML. Forme, taille, couleur et animations sont entièrement dans ton CSS — le JS ne connaît que l'id, le display et les left/top. |
| Périmètre d'écoute (#zone-glow) | div de 300 px de haut avec overflow: hidden | Les trois écouteurs sont attachés à #zone-glow. Change ses dimensions ou son border-radius : overflow: hidden rogné l'orbe différemment, la mécanique JS ne change pas. Pour plusieurs zones, partage un seul #cursorGlow — voir FAQ. |
| Présence des écouteurs mouseenter et mouseleave | les deux actifs, en miroir strict | Supprimer mouseleave laisse l'orbe visible en permanence une fois entré dans la zone — pertinent si la zone couvre l'essentiel du viewport. Garder seulement mousemove sans les deux autres crée un orbe qui suit mais ne s'affiche jamais : il faut aussi passer le display initial à block. |
FAQ
clientX / clientY donnent la position dans le viewport, indépendante du défilement. pageX inclurait le scroll — l'orbe dériverait vers le bas. offsetX varie selon l'élément réellement survolé : un enfant de #zone-glow provoquerait des sauts. clientX − 40 reste stable parce que #cursorGlow en position: fixed est lui aussi relatif au viewport.display: none → block est binaire, il ne peut pas être transitionné. Pour un fondu, initialise #cursorGlow avec opacity: 0; visibility: hidden; transition: opacity .2s. Sur mouseenter, passe à opacity: 1; visibility: visible ; sur mouseleave, l'inverse. visibility: hidden préserve les dimensions de l'élément mais le retire du flux pointer-events, évitant les conflits si la zone et l'orbe se chevauchent.#cursorGlow dans le DOM et attache le même triplet d'écouteurs à chaque zone : l'orbe apparaît et se repositionne dans celle où le pointeur entre, disparaît au mouseleave. Si les zones ne se chevauchent pas, la transition est naturelle. En cas de chevauchement, un compteur de zones actives évite que le mouseleave de l'une masque l'orbe entré dans l'autre.