Retour au catalogue

Hero Cursor Mask

Hero à double couche avec effet masque curseur : une version dark et une version light superposées. La version light est révélée via un clipPath circle animé qui suit la souris (useSpring). Inspiré de la technique Olivier Larose. Effet wow garanti au premier survol.

herocomplex Both Responsive a11y
boldeleganteditorialagencyportfoliosaascentered
Theme

Créer un hero React qui se révèle au curseur

Un hero React qui se révèle au curseur empile deux couches et découpe celle du dessus avec un clip-path circulaire qui suit le pointeur via un spring Framer Motion, révélant la couche du dessous. On le construit avec useMotionValue pour la position et useSpring pour le rayon.

  • Stack : React + Framer Motion + Tailwind v4, ~290 lignes, zéro dépendance supplémentaire.
  • API clé : useMotionValue, useSpring, useMotionTemplate, clip-path.
  • Accessible : les deux couches portent le même texte lisible ; le masque est décoratif.
  • Nécessite un fallback statique sur mobile (pas de pointeur).

Hero Cursor Mask est un hero React plein écran où déplacer le curseur révèle une seconde couche, plus claire, à travers une fenêtre circulaire animée en spring. Il transforme un titre statique en une surface tactile qui invite à explorer, le détail qui fait qu'une landing paraît façonnée à la main plutôt que sortie d'un template.

Anatomie

Deux couches de contenu identiques sont empilées en absolu : une couche sombre de base (fond + grille de points + vignette radiale) et une couche claire au-dessus. La couche claire est masquée partout sauf à l'intérieur d'un clip-path circulaire qui suit le pointeur. Un point d'accent de 12px sert de curseur custom, fusionné en mix-blend-difference pour rester visible sur les deux couches.

Comment ça marche

L'effet repose sur useMotionValue + useSpring de Framer Motion. Les coordonnées du pointeur alimentent deux motion values (mouseX/mouseY) ; le rayon du cercle est son propre spring (stiffness 200, damping 28) qui passe de 0 à 180px à l'entrée et se referme à la sortie. useMotionTemplate les assemble en une chaîne clip-path vivante `circle(${size}px at ${x}px ${y}px)`, pour que la révélation suive le curseur avec une inertie naturelle au lieu de claquer.

Comment le coder en React

  1. Empile deux couches plein écran

    Rends le même contenu deux fois dans un conteneur relative en overflow:hidden. La couche du bas utilise ton thème sombre, celle du haut le clair. Mets cursor:none sur le conteneur, tu dessineras le tien.

  2. Suis le pointeur avec des motion values

    Au mousemove, convertis clientX/Y en coordonnées relatives au conteneur via getBoundingClientRect et écris-les dans les motion values mouseX/mouseY. Garde un spring séparé pour le rayon afin qu'il s'ouvre et se ferme en douceur.

    const mouseX = useMotionValue(0);
    const size = useSpring(rawSize, { stiffness: 200, damping: 28 });
    const clipPath = useMotionTemplate`circle(${size}px at ${mouseX}px ${mouseY}px)`;
  3. Découpe la couche du haut

    Applique le clipPath animé (et -webkit-clip-path) à la couche claire. Ajoute une petite motion.div positionnée en mouseX/mouseY avec mixBlendMode:'difference' comme curseur custom.

Quand l'utiliser

À utiliser sur un hero de landing produit/marque où tu veux une interaction mémorable au-dessus de la ligne de flottaison, agences, outils de design, produits IA. À éviter sur les pages denses ou critiques pour la conversion où un CTA clair prime sur l'effet, et prévois un fallback statique sur mobile (pas de curseur à suivre).

Utilisé par

  • Linear, Surfaces réactives au curseur et révélations spotlight sur tout son site marketing.
  • Vercel, Dégradés pilotés au pointeur et highlights masqués dans les heros.
  • Family, Interactions ludiques menées au curseur comme signature de marque.

FAQ

Le masque curseur marche-t-il sur mobile ?

Non, pas de pointeur sur écran tactile, donc affiche une version statique de la couche claire ou sombre en fallback mobile et désactive les écouteurs mousemove.

Pourquoi un spring plutôt qu'une transition CSS ?

Le spring donne du poids et de l'inertie à la révélation pour qu'elle suive le curseur naturellement ; une transition CSS linéaire paraît mécanique et traîne uniformément quelle que soit la vitesse.

Est-ce accessible ?

Les deux couches contiennent le même texte lisible, donc lecteurs d'écran et utilisateurs sans JS gardent tout le contenu ; le masque est purement décoratif et la couche sombre se suffit à elle-même.

"use client";

import {
  motion,
  useMotionTemplate,
  useMotionValue,
  useSpring,
} from "framer-motion";
import { ArrowRight } from "lucide-react";
import { useCallback, useRef } from "react";

interface HeroCursorMaskProps {
  badge?: string;
  title?: string;
  titleAccent?: string;
  description?: string;
  ctaLabel?: string;
  ctaUrl?: string;
}

const EASE = [0.16, 1, 0.3, 1] as const;
const CIRCLE_EXPANDED = 180;

Code complet réservé à Pro

Code source intégral, export multi-framework et playground.

Passer en Pro, 9,99€/mois

Avis

Hero React au curseur (masque clip-path), Code + Tutoriel