Card Morph
Une carte compacte (pastille, nom, rôle) qui, au clic, s'élargit de 200 à 300 px avec un rebond et déroule un paragraphe et trois chiffres en dégradé ; un second clic la replie. Le contenu se révèle dans la même carte, sans changer de page : l'en-tête est un vrai bouton qui dit si la carte est ouverte, et les détails repliés sont cachés aux lecteurs d'écran.
Mis à jour le
Cet effet fait partie d'Effect.Labs — 840 effets vanilla, une partie gratuite, une partie premium. Liquid comprend 31 effets, dont 6 gratuits. Découvrir la catégorie →
3 exemples d'usage



Comment ça marche
La carte .card-morph mesure 200 px de large, avec 20 px de marge intérieure, un fond violet translucide et des coins de 16 px. Son en-tête .card-header est un <button> qui contient la pastille de 40 px (initiales sur dégradé violet-rose), le nom et le rôle. Dessous, .card-details attend replié : max-height: 0, opacity: 0. Au clic, le script bascule .expanded sur la carte : elle passe à 300 px avec transition: .6s cubic-bezier(.68, -.55, .27, 1.55), un rebond qui la fait légèrement rétrécir puis dépasser sa largeur ; les détails s'ouvrent (max-height jusqu'à 200 px en 0,6 s, opacité et marge en 0,4 s).
Toute la carte écoute le clic, mais seul l'en-tête est un bouton. C'est un choix : un bouton qui engloberait le paragraphe et les chiffres ferait lire tout ce texte comme son nom. Au clavier, Tab atteint l'en-tête, Entrée ou Espace déclenche un clic qui remonte jusqu'à la carte, et aria-expanded passe à true ou false. Repliés, les détails sont aussi rendus invisibles (visibility: hidden, appliqué 0,6 s après le début de la fermeture pour ne pas couper l'animation) : un lecteur d'écran ne lit pas un texte caché à l'écran.
Les trois chiffres sont écrits en dégradé violet-cyan par background-clip: text (avec sa forme -webkit-) et -webkit-text-fill-color: transparent. La hauteur passe par max-height, parce qu'une hauteur auto ne s'anime pas partout : 200 px laissent la place à un paragraphe de trois ou quatre lignes et à la rangée de chiffres ; un contenu plus long serait coupé, montez alors la valeur. Le script, une dizaine de lignes, parcourt toutes les .fx-0406-root .card-morph : plusieurs cartes cohabitent, chacune avec son état.
Accessibilité
- Clavier : mesuré dans Chromium 151 et Firefox 153, Tab atteint l'en-tête, Entrée comme Espace ouvre puis referme la carte, et
aria-expandedsuit. L'anneau de focus (2 px violet, décalé de 4 px) reste dans la marge de la carte. Dans Safari, Tab ne s'arrête sur les boutons que si le réglage clavier le demande (ou avec Option+Tab). - Lecteurs d'écran : l'en-tête est annoncé « Alice Laurent Designer UI/UX, bouton, réduit » ; la pastille d'initiales est masquée (
aria-hidden). Repliés, le paragraphe et les chiffres ne sont pas lus. - Mouvement réduit : sous
prefers-reduced-motion: reduce, la carte prend directement 300 px, sans rebond, et les détails apparaissent en fondu de 0,3 s. Mesuré : plus aucune image ne change après 300 ms. - Contraste : le paragraphe est blanc à 60 % sur fond sombre (environ 7:1) ; les intitulés des chiffres, blancs à 40 %, descendent vers 4:1. Gardez-les courts, ou montez leur opacité si votre fond est plus clair.
Compatibilité navigateur
CSS (transitions de width, max-height, opacity ; background-clip: text) + 0,5 Ko de JavaScript, aucune dépendance. Mesuré image par image dans Chromium 151, Firefox 153 et WebKit 26 : même élargissement avec rebond, même déroulé des détails, même arrêt sous mouvement réduit. Le fond en dégradé de la carte ne s'anime pas : il change d'un coup, sous la largeur qui, elle, glisse.
Sans JavaScript, la carte reste repliée et ses détails ne sont pas accessibles : si le paragraphe et les chiffres comptent, prévoyez un lien vers une page complète. Sans background-clip: text, les chiffres s'affichent en dégradé plein (des rectangles colorés) : remplacez-les alors par une couleur unie.
Le code
Structure HTML à coller dans votre page (le CSS + JS sont inclus avec votre achat) :
<div class="fx-0406-root">
<div class="card-morph-wrapper">
<div class="card-morph">
<button type="button" class="card-header" aria-expanded="false">
<span class="card-avatar" aria-hidden="true">AL</span>
<span class="card-id"><span class="card-name">…</span><span class="card-role">…</span></span>
</button>
<div class="card-details">
<p>…</p>
<div class="card-stats">
<div class="card-stat"><div class="card-stat-value">142</div><div class="card-stat-label">…</div></div> … (3 chiffres)
</div>
</div>
</div>
</div>
</div>
Paiement unique, sans abonnement — le code est à toi pour toujours
Personnaliser
Options passées à l'API ou attributs data-* :
| Option / propriété | Défaut | Effet |
|---|---|---|
Largeurs (CSS — width de .card-morph et de .expanded) |
200 px repliée, 300 px dépliée | L'écart fait l'effet : 220 → 340 px pour un texte plus long. Sur mobile, width: min(300px, 100%) évite que la carte dépliée dépasse l'écran. |
Hauteur maximale des détails (CSS — max-height de .expanded .card-details) |
200 px | Montez-la si votre paragraphe dépasse quatre lignes (300 px pour six lignes). Une valeur trop grande retarde la fermeture : la transition part de cette valeur, pas de la hauteur réelle. |
Rebond (CSS — transition: .6s cubic-bezier(.68, -.55, .27, 1.55)) |
0,6 s, recul puis dépassement | .4s ease donne une ouverture sans rebond. Gardez la même durée que max-height pour que largeur et hauteur arrivent ensemble. |
Teintes (CSS — background de la carte, de .card-avatar et de .card-stat-value) |
violet #a855f7, cyan #06b6d4, rose #ec4899 |
Accordez les trois à votre marque. La carte dépliée prend un fond un peu plus dense et une ombre de 60 px : gardez cet écart, il dit qu'elle est ouverte. |
Contenu (HTML — .card-details) |
un paragraphe et trois chiffres | Mettez-y ce que vous voulez (liste, lien, bouton). Un lien ou un bouton placé dans les détails reçoit le clic et le transmet à la carte : ajoutez e.stopPropagation() sur son clic pour qu'il ne replie pas la carte. |
FAQ
aria-expanded dit l'état, et la souris peut toujours cliquer n'importe où sur la carte, puisque c'est la carte qui écoute le clic.height: 0 à height: auto ne s'anime pas dans tous les navigateurs : elle saute. max-height de 0 à 200 px s'anime partout, au prix d'une limite : un contenu plus haut que 200 px est coupé, et la fermeture commence par « consommer » l'écart entre 200 px et la hauteur réelle. Chrome 129 sait animer vers auto avec interpolate-size: allow-keywords ; tant que Firefox et Safari ne suivent pas, max-height reste le choix qui marche partout.