CartesGratuit

3D Tilt Effect

Rotation 3D temps réel sur deux axes depuis la position du curseur — le contenu flotte en avant et une ombre indigo apparaît au survol.

JS3DInteractive
3D Tilt
Survolez ou cliquez la scène pour interagir

Cet effet est gratuit — la catégorie Cartes en contient 41 au total, dont 6 gratuits. Effect.Labs regroupe 811 effets vanilla. Découvrir la catégorie →

Exemples d'usage

Populaire Pro 29 € / mois

Survolez la carte pour l'incliner

① Grille de cartes tarification — pricing page SaaS

QuandL'utilisateur compare des formules sur une pricing page ou parcourt une grille de fiches produit.
PourquoiLe tilt 3D au survol donne à chaque carte une présence physique et signale l'interactivité — l'ombre indigo dynamique renforce la hiérarchie visuelle sans alourdir la mise en page.
RéglagesDiviseur à 12 (inclinaison modérée ≈ 7°), transition: transform 0.12s, translateZ(20px) pour un relief discret adapté à une grille dense.

Nouvelle version

🚀 Livrez plus vite avec Effect.Labs Effets interactifs · 0 dépendance

Survolez la carte pour l'incliner

② Carte hero centrée — landing page ou portfolio

QuandUne unique grande carte occupe l'espace central du hero, présentant un visuel fort, un chiffre-clé ou une démo produit.
PourquoiSeule sur une grande surface, la carte exploite pleinement la perspective 1000 px et le parallaxe du translateZ — l'interaction souris devient la démonstration d'interactivité de la page.
RéglagesDiviseur à 8 (inclinaison prononcée ≈ 11°), carte agrandie à 400×260 px, translateZ(50px) pour un contenu très flottant.
👩‍💻 Sarah Chen Lead Designer UI/UX Motion

Survolez la carte pour l'incliner

③ Galerie de membres — page Équipe ou intervenants

QuandLes membres d'une équipe ou les intervenants d'un événement sont présentés en grille avec photo, nom et rôle.
PourquoiLe tilt ajoute une touche interactive mémorable à un contenu habituellement statique — la carte se soulève légèrement comme si elle prenait vie sous le curseur.
RéglagesDiviseur à 15 (tilt doux ≈ 6°), transition: transform 0.18s, ombre portée à la couleur primaire de la marque.

Comment ça marche

À chaque mousemove, le script calcule la position du curseur relative au centre de la carte via getBoundingClientRect(). Deux angles sont dérivés : rotateX = (y − centreY) / 10 (inclinaison avant/arrière) et rotateY = (centreX − x) / 10 (inclinaison gauche/droite). Le diviseur 10 plafonne l'inclinaison à environ ±9° en bord de carte.

La transformation est appliquée en une seule chaîne inline : perspective(1000px) rotateX(Xdeg) rotateY(Ydeg). Utiliser perspective() dans la fonction transform plutôt que comme propriété CSS parente lie le point de fuite à l'élément lui-même — chaque carte dans une grille a sa propre perspective, sans interférence.

La propriété CSS transform-style: preserve-3d sur .card-tilt propage le contexte 3D aux enfants. L'élément .tilt-content reçoit translateZ(30px), ce qui le fait flotter visuellement à 30 px devant la surface de la carte — un effet de parallaxe discret entre le fond de la carte et son contenu.

Au mouseleave, le transform revient à perspective(1000px) rotateX(0) rotateY(0) grâce à la transition: transform 0.1s définie en CSS — le rappel est fluide en 100 ms. L'ombre portée indigo du sélecteur .card-tilt:hover suit le même mécanisme CSS natif sans JS supplémentaire.

Accessibilité

  • prefers-reduced-motion absent du code : l'inclinaison se déclenche toujours au survol, sans tenir compte de la préférence système. Ajoutez @media (prefers-reduced-motion: reduce) { .card-tilt { transition: none; } } et désactivez les listeners JS si nécessaire.
  • L'effet est exclusivement à la souris (mousemove / mouseleave) — aucune interaction clavier ni touch. La carte reste accessible et lisible sans interaction.
  • Aucun attribut ARIA n'est ajouté par l'effet. Si la carte est interactive (lien, bouton), ajoutez role, aria-label ou aria-labelledby manuellement.
  • Le contraste du contenu textuel n'est pas affecté par l'inclinaison : couleurs et luminosité restent constantes quelle que soit la rotation.
  • L'effet n'intercepte aucun événement clavier et ne piège pas le focus — les éléments enfants interactifs (liens, boutons) restent focusables normalement.

Compatibilité navigateur

Nécessite CSS transform-style: preserve-3d et getBoundingClientRect — disponibles dans tous les navigateurs modernes. Zéro dépendance externe.

Chrome 36+✓ Complet
Firefox 16+✓ Complet
Safari 9+✓ Complet
Edge 79+✓ Complet
Mobile iOS✓ Statique (pas de hover)
Android Chrome✓ Statique (pas de hover)

Sans support des transformations 3D CSS, la carte s'affiche à plat — aucune erreur JS, le contenu reste lisible et accessible.

Le code

Copiez les trois blocs dans votre page. Aucune dépendance.

HTML
<div class="card-demo card-tilt" id="tiltCard">
  <span class="tilt-content">3D Tilt</span>
</div>
CSS
.card-demo  {
   width: 280px;
   height: 180px;
   background: var(--bg-card);
   border-radius: var(--radius-xl);
   display: flex;
   align-items: center;
   justify-content: center;
   font-weight: 600;
   position: relative;
   cursor: pointer;
   }

.card-tilt  {
   transform-style: preserve-3d;
   transition: transform 0.1s;
   }

.card-tilt:hover  {
   box-shadow: rgba(99, 102, 241, 0.3) 0px 25px 50px;
   }

.card-tilt .tilt-content  {
   transform: translateZ(30px);
   }

/* Variables du design system du site, dont ce code depend. Sans elles,
   l'acheteur obtenait des coins carres et des couleurs par defaut (revue Owen).
   Valeurs reprises de css/style.css — a adapter librement. */
.card-demo, .card-tilt {
  --bg-card: #12121a;
  --radius-xl: 1rem;
}

/* Le fond de carte est desormais sombre : sans couleur de texte declaree,
   le libelle heritait du noir de la page hote et devenait illisible (revue Owen). */
.card-demo { color: #fff; }
JavaScript (fx-0128)
(function(){
    // 3D Tilt Effect
    const tiltCard = document.getElementById('tiltCard');
    if (tiltCard) {
      tiltCard.addEventListener('mousemove', (e) => {
        const rect = tiltCard.getBoundingClientRect();
        const x = e.clientX - rect.left;
        const y = e.clientY - rect.top;
        const centerX = rect.width / 2;
        const centerY = rect.height / 2;
        const rotateX = (y - centerY) / 10;
        const rotateY = (centerX - x) / 10;
        tiltCard.style.transform = `perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`;
      });
      tiltCard.addEventListener('mouseleave', () => {
        tiltCard.style.transform = 'perspective(1000px) rotateX(0) rotateY(0)';
      });
    }

    // Spotlight Effect
    const spotlightCard = document.getElementById('spotlightCard');
    if (spotlightCard) {
      spotlightCard.addEventListener('mousemove', (e) => {
        const rect = spotlightCard.getBoundingClientRect();
        const x = e.clientX - rect.left;
        const y = e.clientY - rect.top;
        spotlightCard.style.setProperty('--spotlight-x', x + 'px');
        spotlightCard.style.setProperty('--spotlight-y', y + 'px');
      });
    }

    // Card Morph Expand Effect
    const morphExpandCard = document.getElementById('morphExpandCard');
    if (morphExpandCard) {
      morphExpandCard.addEventListener('click', () => {
        morphExpandCard.classList.toggle('expanded');
      });
      // Close when clicking outside
      document.addEventListener('click', (e) => {
        if (!morphExpandCard.contains(e.target) && morphExpandCard.classList.contains('expanded')) {
          morphExpandCard.classList.remove('expanded');
        }
      });
    }
  })();

Personnaliser

Options passées à l'API ou attributs data-* :

Option / propriétéDéfautEffet
Diviseur /10 (JS — lignes rotateX / rotateY) 10 Amplitude maximale du tilt. Augmentez pour adoucir (15 → ~6°), diminuez pour accentuer (6 → ~15°).
perspective(1000px) (JS — chaîne transform) 1000px Distance de la caméra virtuelle. Plus élevé = perspective plus plate ; plus bas = déformation 3D plus prononcée.
transition: transform 0.1s (CSS — .card-tilt) 0.1s Vitesse de suivi du curseur. 0.05s = snap vif, 0.25s = suivi lent et langoureux.
translateZ(30px) (CSS — .card-tilt .tilt-content) 30px Profondeur de flottement du contenu devant la surface de la carte. Augmentez jusqu'à 60px pour un effet holographique.
box-shadow (CSS — .card-tilt:hover) rgba(99,102,241,0.3) 0px 25px 50px Couleur et intensité de l'ombre portée au survol. Remplacez la couleur par votre couleur primaire brand.
--bg-card (variable CSS) définie globalement Couleur ou dégradé de fond de la carte — redéfinissez la variable sur la carte ou son parent pour personnaliser.
width / height (CSS — .card-demo) 280px / 180px Dimensions de la carte. Le calcul de rotation utilise getBoundingClientRect — redimensionner ne casse pas l'effet.

FAQ

L'effet fonctionne-t-il sur mobile (touch) ?

Non — le code écoute mousemove et mouseleave, qui ne se déclenchent pas au toucher. Sur mobile, la carte s'affiche sans inclinaison. Pour ajouter le support touch, écoutez touchmove et calculez la rotation depuis e.touches[0].clientX/Y de la même façon.

Comment appliquer le tilt à plusieurs cartes sur la même page ?

Le code fourni cible un unique élément par son id. Pour plusieurs instances, sélectionnez par classe : document.querySelectorAll('.card-tilt'), puis attachez mousemove et mouseleave à chaque élément dans une boucle — chaque carte calcule ses propres coordonnées via getBoundingClientRect().

Les éléments enfants (boutons, liens) restent-ils cliquables à l'intérieur d'une carte inclinée ?

Oui. La transformation 3D est appliquée via element.style.transform — elle n'intercepte ni les clics ni les événements DOM des enfants. Un bouton ou un lien à l'intérieur de la carte reste pleinement interactif et focusable au clavier.