Skip to content
← 1001 Widgets
N° 009 · ●●○

Shine card

A card that tilts towards the pointer, with a shine that slides across its surface. The effect of the cards on this page, taken apart piece by piece.

HTMLCSSJS
What you'll learn
  • Drive CSS variables from JS
  • Throttle the work with requestAnimationFrame
  • Handle mouse and touch with Pointer Events
Why this language

Pourquoi une carte à reflet ? C'est l'effet des cartes de ce site : la carte s'incline vers le pointeur et une bande de lumière glisse sur sa surface, comme sur une carte holographique.

Tout tient dans trois variables CSS mises à jour par JavaScript : deux angles d'inclinaison et la position du reflet. Le CSS fait le reste.

Pourquoi des variables CSS ? JavaScript ne touche jamais au style directement : il donne trois nombres, et le CSS décide de ce qu'il en fait.

How it works
  1. LE HTML

    Le reflet est un calque posé sur la carte.

    <div class="carte" id="carte">
      <h3>N° 009</h3>
      <div class="reflet"></div>
    </div>
  2. LE CSS

    L'inclinaison et le reflet lisent des variables. Par défaut, la carte est à plat.

    .carte { --rx: 0deg; --ry: 0deg; --px: 50%; position: relative; overflow: hidden; transform: perspective(800px) rotateX(var(--rx)) rotateY(var(--ry)); transition: transform .5s cubic-bezier(.16, 1, .3, 1); }
    .carte.bouge { transition: transform .08s linear; }
    .reflet { position: absolute; inset: 0; pointer-events: none; background: linear-gradient(105deg, transparent calc(var(--px) - 20%), rgba(255, 255, 255, .18) var(--px), transparent calc(var(--px) + 20%)); }
  3. SUIVRE LE POINTEUR

    On ramène la position du pointeur entre -0,5 et 0,5, le centre de la carte valant 0.

    const carte = document.getElementById('carte');
    carte.addEventListener('pointermove', e => {
      const r = carte.getBoundingClientRect();
      const x = (e.clientX - r.left) / r.width - 0.5;
      const y = (e.clientY - r.top) / r.height - 0.5;
      carte.classList.add('bouge');
      carte.style.setProperty('--ry', `${x * 16}deg`);
      carte.style.setProperty('--rx', `${-y * 16}deg`);
      carte.style.setProperty('--px', `${(x + 0.5) * 100}%`);
    });
  4. REVENIR À PLAT

    On retire la classe bouge : la transition longue reprend et la carte se repose en douceur.

    carte.addEventListener('pointerleave', () => {
      carte.classList.remove('bouge');
      carte.style.setProperty('--rx', '0deg');
      carte.style.setProperty('--ry', '0deg');
      carte.style.setProperty('--px', '50%');
    });
Pitfalls

PIÈGE 1 : Une carte qui suit avec retard

Pitfall

Mauvais : garder la même transition longue pendant le mouvement. La carte suit le pointeur avec un temps de retard mou.

Fix

Correct : une transition très courte pendant le mouvement (la classe bouge), et la transition longue seulement au retour à plat.

PIÈGE 2 : Le reflet qui bloque les clics

Pitfall

Mauvais : un calque de reflet par-dessus la carte : plus aucun bouton ne répond.

Fix

Correct : pointer-events: none sur le calque.

PIÈGE 3 : mousemove sur mobile

Pitfall

Mauvais : écouter mousemove. Au doigt, rien ne bouge.

Fix

Correct : pointermove couvre la souris, le doigt et le stylet. Ajoutez touch-action: none sur la carte pour que le doigt ne fasse pas défiler la page.

PERSONNALISER
  • L'angle maximal : changez le 16 pour une carte plus calme ou plus vive.
  • La couleur du reflet : remplacez le blanc par une teinte, ou superposez deux bandes de couleurs différentes pour un effet holographique.
  • Une ombre décalée à l'opposé de l'inclinaison renforce le relief.
OPTIMISATION
  • Regroupez les mises à jour avec requestAnimationFrame : une seule écriture par image, même si le pointeur envoie plus d'événements (c'est ce que fait la démo).
  • Avec prefers-reduced-motion, gardez le reflet mais supprimez l'inclinaison.
VERS REACT

Un useRef sur la carte, et des écouteurs qui écrivent les variables CSS directement.

Pas de useState pour la position : sinon chaque mouvement redessine tout le composant.

Variants
Comments
0/1000

Loading comments…

Audience measurement

With your consent, this site measures its traffic with Google Analytics and the Meta pixel, via Google Tag Manager. Nothing is loaded until you choose, and you can change your mind at any time with the "Cookies" link at the bottom of the page.