Curseurs✨ Premium

Custom Circle Cursor

Un cercle substitué au curseur natif dans une zone délimitée : `mouseenter` l'affiche et efface le pointeur système, `mousemove` le téléporte à la coordonnée exacte du pointeur — zéro interpolation, zéro délai — et teste à chaque événement si la cible porte l'attribut `data-hover` pour basculer l'état visuel du cercle.

JSBlend

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

Galerie photo en fond sombre — portfolio créatif — Custom Circle Cursor exemple 1

① Galerie photo en fond sombre — portfolio créatif

QuandUn portfolio photo présente des visuels plein-écran sur fond noir et veut une signature d'interaction qui ne repose ni sur tooltip ni sur overlay.
Pourquoi<code>mix-blend-mode: difference</code> sur le cercle inverse les couleurs sous le pointeur : zones claires de l'image deviennent sombres, zones sombres deviennent claires. Sans ajouter de JS supplémentaire, le cercle révèle la texture de l'image à mesure que le visiteur se déplace.
RéglagesCercle de 80–100 px ; difference ou exclusion selon le contraste voulu ; restreins la zone au conteneur de la galerie pour garder le curseur natif dans la navigation.
Canvas interactif ou panneau de visualisation — outil web — Custom Circle Cursor exemple 2

② Canvas interactif ou panneau de visualisation — outil web

QuandUn éditeur ou dashboard interne veut signaler que l'utilisateur est entré dans une zone de travail avec des règles d'interaction différentes du reste de l'interface.
PourquoiLe curseur custom délimite visuellement la zone active sans bordure ni couleur de fond supplémentaire. Le changement d'état <code>hover</code> peut signaler les handles ou éléments éditables dès le survol, sans tooltip additionnel.
RéglagesCercle fin (2 px de bordure, fond transparent) ; mix-blend-mode: normal avec une couleur distinctive ; pose data-hover sur les poignées et zones sélectionnables.
Section scrollytelling ou hero interactif — site éditorial — Custom Circle Cursor exemple 3

③ Section scrollytelling ou hero interactif — site éditorial

QuandUne page longform utilise le déplacement de la souris comme vecteur narratif : le visiteur révèle des annotations ou interagit avec une illustration en déplaçant le curseur.
PourquoiLe cercle signale que le mouvement est actif et signifiant, sans instruction textuelle. Son état <code>hover</code> change au-dessus des zones annotées (portant <code>data-hover</code>), invitant le clic. L'effet reste dans la zone éditoriale uniquement.
RéglagesCercle de 60 px avec légère opacité ; mix-blend-mode: multiply sur fond clair pour un rendu encre ; le changement d'état sur data-hover peut déclencher une animation CSS annexe — le cercle joue le déclencheur.

Comment ça marche

Trois écouteurs sont posés sur le conteneur #zone-circle, jamais sur le cercle lui-même. mouseenter déclenche deux actions : il ajoute la classe cursor-active à la zone — ce qui active la règle CSS cursor: none et efface le pointeur système — puis passe le cercle de display: none à display: block. mouseleave inverse tout : retire cursor-active, remet le cercle à display: none et supprime hover pour un état propre à la prochaine entrée.

À chaque mousemove, le cercle reçoit deux valeurs : style.left = e.clientX + 'px' et style.top = e.clientY + 'px'. Ce sont des coordonnées viewport écrites directement, sans coefficient, sans requestAnimationFrame, sans lerp. Le cercle ne suit pas le curseur avec délai ou traîne — il occupe la même position que lui à chaque instant. Le centrage au point de contact est géré par transform: translate(-50%, -50%) côté CSS. La propriété mix-blend-mode déclarée sur le cercle fusionne visuellement le cercle avec le contenu en dessous (inversion, soustraction, multiplication) — sans calcul JS.

Toujours dans le mousemove, après repositionnement, le code inspecte e.target : si cet élément possède l'attribut data-hover, la classe hover est ajoutée au cercle ; sinon, elle est retirée. Ce contrôle s'exécute à chaque événement, sans cache ni délai — le basculement est instantané. Le JS ne connaît pas l'apparence du cercle : il fournit un état binaire que le CSS interprète visuellement. L'attribut data-hover est ton sélecteur sémantique — tu désignes les éléments actifs dans la zone sans toucher au JS.

Accessibilité

  • Masquer le curseur natif est une décision d'accessibilité lourde, pas une simple animation. Si le cercle custom ne se charge pas (JS bloqué, erreur de rendu), cursor: none prive l'utilisateur de son repère sans rien lui substituer. Vérifie que #cursorCircle est présent dans le DOM avant de démarrer les écouteurs.
  • Sur clavier, mousemove ne se déclenche jamais : l'utilisateur ne voit pas le cercle, et si cursor: none s'applique via une autre règle, le repère visuel disparaît sans substitut. Assure-toi que chaque élément cliquable dans la zone dispose d'un état :focus-visible explicite — anneau de focus, ombre — indépendant de l'effet curseur.
  • Sur écran tactile, aucun événement mouse* ne circule : le cercle reste à display: none, la zone conserve son curseur natif. Repli propre, aucun code à écrire. Pour bloquer les mousemove émulés au tap, conditionne les écouteurs à matchMedia('(hover: hover) and (pointer: fine)').
  • Le cercle est un ornement purement visuel. Assure-toi qu'il porte pointer-events: none en CSS, sinon il intercepte les événements mouse* à sa propre position et casse la détection de data-hover sur les éléments en dessous. Ne porte aucune information par le cercle ou son état hover seul — une partie de ton audience ne verra jamais ni l'un ni l'autre.

Compatibilité navigateur

Toutes les APIs utilisées sont ES5-compatibles : getElementById, classList, addEventListener, style.left/top/display, hasAttribute. Le seul plancher est syntaxique : les fonctions fléchées (ES2015) bloquent IE 11. Côté CSS, mix-blend-mode est le critère limitant — absent de IE et Edge pré-Chromium, où le cercle s'affiche sans fusion.

Chrome 79+✓ Complet
Firefox 72+✓ Complet
Safari 13.1+✓ Complet
Edge 79+✓ Complet
Mobile iOS✓ Inerte (tactile)
Android Chrome✓ Inerte (tactile)

Sans JavaScript, le cercle n'est jamais rendu visible et <code>cursor-active</code> n'est jamais ajouté : le pointeur natif reste intact. Sans <code>mix-blend-mode</code> (Edge 18 et antérieur), le cercle s'affiche en aplat opaque — un repli fonctionnel si tu lui assignes une couleur de fond lisible. Aucun code de secours à écrire dans les deux cas.

Le code

Structure HTML à coller dans votre page (CSS + JS disponibles avec un compte premium) :

index.html — structure
🔒 Débloquer le code complet — à partir de 2,99 € le 1er mois

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éfautEffet
Zone d'activation (#zone-circle) le conteneur de 300 px de haut dans la démo Les trois écouteurs (mouseenter, mouseleave, mousemove) sont posés sur cet élément. Remplace l'id par n'importe quel conteneur ou par document.body pour un curseur pleine page.
Taille du cercle (CSS width/height sur #cursorCircle) définie en CSS, non codée en dur dans le JS Le JavaScript n'écrit que left et top — il ignore la taille du cercle. Modifie librement width et height en CSS, le centrage reste assuré par transform: translate(-50%, -50%).
mix-blend-mode (CSS sur #cursorCircle) défini en CSS — 'difference' dans la démo difference inverse les couleurs sous le cercle. exclusion donne un résultat similaire mais plus doux. multiply assombrit (fond clair). screen éclaircit (fond sombre). normal désactive la fusion pour un aplat opaque — préférable dans les interfaces de travail.
L'attribut data-hover présent sur le bouton de la démo (data-hover="true") À chaque mousemove, le code teste e.target.hasAttribute('data-hover') — seule la présence compte, pas la valeur. Pose-le sur n'importe quel élément HTML (lien, image, SVG, bouton) pour déclencher hover sur le cercle.
La classe .hover sur #cursorCircle (CSS) basculée par data-hover — apparence entièrement à définir en CSS Le JS ajoute ou retire .hover ; tout le rendu visuel dépend du CSS que tu écris pour ce sélecteur. Exemples : scale(1.6) pour agrandir le cercle au-dessus des éléments actifs ; changement de mix-blend-mode selon l'état ; réduction d'opacité pour un effet d'effacement.
La classe .cursor-active (CSS sur #zone-circle) ajoutée par mouseenter, retirée par mouseleave C'est cette classe qui doit porter cursor: none dans ton CSS — le JS ne touche jamais directement à la propriété curseur. Si tu veux masquer le pointeur natif hors zone également, déplace cursor: none sur le body et réserve la classe à d'autres styles de zone.

FAQ

Le JS ajoute cursor-active au conteneur #zone-circle dès que le pointeur entre dedans. Une règle CSS sur ce sélecteur déclare cursor: none — le JS ne touche jamais la propriété directement. Au mouseleave, la classe est retirée et le curseur natif reprend ses droits immédiatement. Sans JavaScript, cursor-active n'est jamais ajouté et le pointeur reste visible.
Non. Le code écrit style.left = e.clientX + 'px' et style.top = e.clientY + 'px' directement à chaque mousemove, sans coefficient ni interpolation. La position du cercle est identique à celle du pointeur à chaque instant — c'est une substitution géométrique, pas un suivi avec friction. Pour une traîne, un lerp par frame ou une transition CSS sur left/top — c'est un effet différent.
Rien, et c'est cohérent avec la mécanique. Sur tactile, mouseenter et mousemove ne circulent pas : le cercle reste à display: none, cursor-active n'est jamais ajouté et le curseur natif n'est jamais masqué. Aucun code de désactivation à écrire. Pour bloquer les mousemove émulés au tap, conditionne les écouteurs à matchMedia('(hover: hover) and (pointer: fine)').