thekof
Tous les articles
React06/08/202614 min

Liquid Glass avec React et TypeScript : interactions au pointeur

Une interaction Liquid Glass réussie répond au pointeur sans transformer chaque pixel en mise à jour React. Le DOM porte les coordonnées, CSS dessine la lumière et React conserve seulement l’état sémantique.

Disque lumineux suivant une trajectoire sur une surface de verre violette

Séparer fréquence et sémantique

Les coordonnées du pointeur changent jusqu’à plusieurs dizaines de fois par seconde. Les stocker dans useState relance le rendu et mélange animation impérative et état applicatif. Écrivez plutôt deux propriétés CSS sur l’élément ciblé. React reste responsable de l’activation, de la désactivation et des variantes.

tsx
function updateGlow(event: React.PointerEvent<HTMLElement>) { const node = event.currentTarget; const rect = node.getBoundingClientRect(); node.style.setProperty("--pointer-x", `${event.clientX - rect.left}px`); node.style.setProperty("--pointer-y", `${event.clientY - rect.top}px`); }

Typez le gestionnaire avec l’élément réellement utilisé. currentTarget reste alors sûr même si target désigne une icône enfant.

Échantillonner avec requestAnimationFrame

Sur une grille riche, regroupez les événements dans une frame. Gardez le dernier événement, annulez la frame en attente au démontage et n’effectuez qu’une lecture de géométrie avant les écritures CSS. Cela réduit les recalculs de layout et garde une trajectoire fluide.

Ne capturez le pointeur que pour un geste qui doit continuer hors de la cible, comme un slider. Un simple reflet de survol n’en a pas besoin. Sur tactile, ignorez généralement l’effet continu : la position du doigt masque la lumière et n’apporte pas de signal fonctionnel.

Dessiner le reflet en CSS

Un pseudo-élément peut utiliser un gradient radial centré sur les variables. Laissez pointer-events: none, limitez l’opacité et masquez le débordement. L’état pressé doit rester distinct : légère réduction d’échelle ou changement de bordure, sans dépendre de la position du reflet.

css
.glass-card::before { content: ""; position: absolute; inset: 0; pointer-events: none; background: radial-gradient(280px at var(--pointer-x) var(--pointer-y), rgb(255 255 255 / 0.22), transparent 65%); opacity: var(--glow-opacity, 0); } .glass-card:hover { --glow-opacity: 1; }

Un composant utilisable partout

Exposez une API courte : intensity, interactive et asChild si votre bibliothèque le permet. N’exposez pas les coordonnées. La surface ne doit pas imposer un rôle ARIA; le composant consommateur choisit lien, bouton ou simple section.

Pour le clavier, :focus-visible fournit un état équivalent au survol. Pour la réduction des mouvements, désactivez interpolation et transformation, mais conservez l’anneau et la bordure. Une préférence utilisateur supprime le mouvement, pas l’information.

Nettoyage et tests

Nettoyez les frames et observateurs au démontage. Testez pointerleave, changement rapide de cible et redimensionnement. Un test de composant peut vérifier que les variables sont écrites; un test navigateur valide la trajectoire, le focus et l’absence de mouvement en préférence réduite.

Utilisez le profileur React : aucun commit ne devrait apparaître pendant un simple déplacement. Dans Performance, surveillez les layouts forcés et le coût de paint.

Cette interaction repose sur la base CSS progressive. L’étape suivante consiste à mesurer performance, compatibilité et accessibilité, avant de l’intégrer au design system React.

À lire ensuite