Curseurs✨ Premium

Cursor Text

Une étiquette fixe de 12 px décalée de 15 px sous le pointeur, qui réécrit à chaque mouvement les coordonnées locales de la souris — « X: 228 Y: 180 », arrondies au pixel, mesurées depuis le coin haut-gauche de la zone. Le curseur natif est masqué : l'étiquette est le seul repère.

JSInfo

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

Plan de travail d'un éditeur — lecture de position en direct — Cursor Text exemple 1

① Plan de travail d'un éditeur — lecture de position en direct

QuandInterface d'outil de design : règles, plan de travail, une forme posée. L'étiquette donne la position du pointeur en pixels.
PourquoiC'est l'usage littéral du code : des coordonnées locales, arrondies, qui suivent la main. L'étiquette monospace lue à 15 px du pointeur ne masque jamais ce que l'on aligne.
RéglagesCouleur #a5b4fc (7,8:1), décalage translate(18px, 18px) pour dégager la poignée de la forme, cursor: none retiré pour garder le réticule système.
Plan de salle — repérage d'un siège — Cursor Text exemple 2

② Plan de salle — repérage d'un siège

QuandSélecteur de places d'une salle de spectacle ou d'un open space : une grille de sièges, une légende.
PourquoiLes coordonnées deviennent un repère de rang et de colonne ; le curseur masqué et l'étiquette claire évitent que la flèche cache un siège de 12 px.
RéglagesTexte remplacé par Rang ${…} · Siège ${…} calculé à partir des coordonnées, fond rgba(15,15,26,.9), couleur #c7d2fe.
Graphique d'analyse — position sur les axes — Cursor Text exemple 3

③ Graphique d'analyse — position sur les axes

QuandTableau de bord : un graphique en barres ou en aires, des axes, une période. L'étiquette lit la position du pointeur.
PourquoiUn rôle de sonde : le lecteur voit où il pointe avant même que l'infobulle de la librairie de graphes n'apparaisse ; l'étiquette légère ne concurrence pas les valeurs affichées.
RéglagesPolice ui-monospace à 11 px, bordure supprimée, décalage réduit à translate(12px, -28px) pour lire au-dessus du pointeur.

Comment ça marche

Un seul élément porte l'effet : #cursorText (.custom-cursor.cursor-text-display). La classe .custom-cursor le sort du flux — position: fixed, pointer-events: none, z-index: 9999, display: none — et .cursor-text-display l'habille : font: 600 .75rem Inter, monospace, couleur #6366f1, fond rgba(99,102,241,.1), bordure 1 px rgba(99,102,241,.3), padding: 4px 8px, border-radius: 4px, white-space: nowrap et surtout transform: translate(15px, 15px), le décalage qui l'écarte du point chaud.

La zone #zone-text reçoit trois écouteurs. mouseenter ajoute la classe cursor-active à la zone — cette fois la règle existe bien dans le CSS livré : .cursor-active { cursor: none !important }, la flèche système disparaît (mesuré : cursor: none) — et passe l'étiquette en display: block. mouseleave défait les deux. mousemove fait le travail : localX = Math.round(clientX − rect.left), idem en Y, puis textContent = `X: ${localX} Y: ${localY}` et left = clientX + 'px', top = clientY + 'px'.

Les coordonnées écrites sont locales à la zone (0, 0 = son coin haut-gauche), recalculées via getBoundingClientRect() à chaque événement — donc justes même si la page a défilé ou si la zone a bougé. Le positionnement, lui, est en coordonnées de fenêtre (clientX/Y, cohérent avec position: fixed). Mesuré : pour un pointeur à (228, 180) dans la zone, le texte lit exactement « X: 228 Y: 180 » et le coin haut-gauche de l'étiquette est à +15 px, +15 px du pointeur ; l'étiquette fait 112 × 24 px.

Ni requestAnimationFrame ni lissage : l'étiquette est collée au pointeur, 0 appel rAF/s hors survol ; le coût par événement est une écriture de textContent (relayout de l'étiquette seule). Le fichier JS livré ne contient que ce bloc (« Zone 9 : Cursor Text »), rien d'un autre effet. La règle .cursor-zone définit localement ses cinq variables : la zone de démo se rend correctement sur une page vierge.

Accessibilité

  • Contraste de l'étiquette insuffisant : #6366f1 sur son fond (rgba(99,102,241,.1) composé sur #1a1a24, soit #212238) = 3,5:1 pour un texte de 12 px, sous le seuil AA de 4,5:1. Passez la couleur à #a5b4fc (7,8:1) ou #c7d2fe (10,4:1) ; #818cf8 (5,2:1) reste le plus proche de l'original.
  • cursor: none fait de l'étiquette le seul indicateur du pointeur, or elle est décalée de 15 px en bas à droite : rien ne marque le point chaud exact. Pour une interface de précision, retirez la règle .cursor-active (la flèche revient, l'étiquette l'accompagne) ou ajoutez un ::before de 4 px à translate(-15px, -15px) qui repose sur le pointeur.
  • Lecteurs d'écran : le div change de texte à chaque mouvement. Il n'a pas d'aria-live, donc rien n'est annoncé, mais il reste dans l'arbre d'accessibilité : ajoutez aria-hidden="true" pour que les technologies d'assistance l'ignorent tout à fait. Si les coordonnées ont un sens fonctionnel (sélecteur de position), exposez-les aussi dans un champ ou une sortie output.
  • Tactile : mesuré avec émulation tactile, un tap dans la zone affiche l'étiquette figée sur les coordonnées du tap et laisse cursor-active en place même après un tap hors zone (pas de mouseleave). Conditionnez l'initialisation à matchMedia('(hover: hover) and (pointer: fine)').matches.
  • Clavier : aucun équivalent, l'étiquette ne se montre jamais — acceptable tant qu'elle n'apporte qu'un confort. Pas de mouvement autonome : prefers-reduced-motion n'a rien à désactiver. z-index: 9999 : l'étiquette passe au-dessus des modales et des menus ouverts.

Compatibilité navigateur

Requiert ES2015 (gabarits de chaîne, fonctions fléchées, const), classList, getBoundingClientRect et les variables CSS de la zone. Zéro dépendance.

Chrome 49+✓ Complet
Firefox 45+✓ Complet
Safari 10+✓ Complet
Edge 15+✓ Complet
Mobile iOS✓ Inactif (étiquette figée au tap)
Android Chrome✓ Inactif (étiquette figée au tap)

Sans JavaScript, l'étiquette reste en display: none et la flèche système est conservée : la zone est une boîte ordinaire. Sans variables CSS, la zone perd son fond et son arrondi ; l'étiquette, qui n'en utilise aucune, reste intacte.

Le code

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

index.html — structure
<div class="cursor-zone" id="zone-text">
  Survolez cette zone
  <span class="info-text">Texte suiveur</span>
</div>
<!-- Ce div est le curseur lui-même : le script le déplace et le fait
     apparaître. Sans lui, le script sort et l'effet ne rend rien. -->
<div class="custom-cursor cursor-text-display" id="cursorText">X: 0 Y: 0</div>
🔒 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
Décalage (CSS — .cursor-text-display transform) translate(15px, 15px) Position de l'étiquette par rapport au point chaud. Des valeurs négatives la placent au-dessus ou à gauche ; 0 la centre sur le pointeur et masque ce qu'il vise.
Contenu (JS — cursorText.textContent = …) X: ${localX} Y: ${localY} Ce que lit l'étiquette. Un texte fixe (« Voir », « Glisser ») se pose une fois ; pour des unités, divisez par rect.width pour des pourcentages.
Couleur du texte (CSS — .cursor-text-display color) #6366f1 3,5:1 sur le fond livré. #a5b4fc ou #c7d2fe passent le seuil de 4,5:1 sans changer la teinte.
Police et taille (CSS — .cursor-text-display font) 600 .75rem Inter, monospace Inter n'est pas fournie : la pile retombe sur monospace. Une monospace assumée (ui-monospace, Menlo) évite le saut de largeur quand les chiffres changent.
Fond et bordure (CSS — .cursor-text-display background, border) rgba(99,102,241,.1) / 1px rgba(99,102,241,.3) Habillage de la pastille. Un fond opaque (#0f0f1a) garantit la lisibilité au-dessus d'une image ; sans bordure, l'étiquette devient un simple texte flottant.
Masquage du curseur natif (CSS — .cursor-active) cursor: none !important Supprimez la règle pour garder la flèche système et n'ajouter que l'étiquette — recommandé dès que la zone contient des cibles de précision.
Arrondi des coordonnées (JS — Math.round) au pixel Math.round(x * 10) / 10 pour un dixième de pixel (utile avec un zoom), ou Math.floor pour rester dans la cellule survolée d'une grille.

FAQ

Remplacez la ligne cursorText.textContent = `X: ${localX} Y: ${localY}` par le texte voulu, ou fixez-le une fois dans le HTML et supprimez cette ligne ainsi que les deux Math.round devenus inutiles. Pour un texte qui dépend de l'élément survolé, lisez e.target.closest('[data-cursor-text]') et copiez son attribut — le reste du code (affichage, position, décalage) ne change pas.
Parce qu'un ancêtre de #cursorText porte transform, filter, perspective, will-change: transform ou contain: paint : il devient le bloc conteneur de l'élément position: fixed, et left = clientX se mesure alors depuis cet ancêtre, plus depuis la fenêtre. Le code livré place l'étiquette dans la zone de démo ; à l'intégration, déplacez-la juste avant </body>, hors de tout conteneur transformé.
À la zone écoutée : rect = zoneText.getBoundingClientRect(), puis clientX − rect.left — (0, 0) est le coin haut-gauche de la zone, indépendamment du défilement. Pour des pourcentages : Math.round((clientX − rect.left) / rect.width × 100). Pour des coordonnées de document : e.pageX et e.pageY directement. Le positionnement de l'étiquette, lui, doit rester en clientX/Y puisqu'elle est fixed.