Clip Path Reveal
Trois blocs de 91 px dans une boîte de 280 px qui défile sur 620 px, chacun révélé par clip-path: inset() vers inset(0) sur une timeline view(), plage entry → cover 60 % : le premier s'ouvre de gauche à droite, le deuxième de haut en bas, le troisième du bas vers le haut, sans fondu ni déplacement — le contenu est déjà là, c'est le cache qui s'ouvre, sur 223 px de défilement par bloc. CSS seul, zéro JavaScript.
Mis à jour le
Cet effet fait partie d'Effect.Labs — 811 effets vanilla, une partie gratuite, une partie premium. Scroll comprend 70 effets, dont 12 gratuits. Découvrir la catégorie →
3 exemples d'usage



Comment ça marche
Le HTML vendu : le cadre .demo-preview (flex centré, min-height: 300px, position: relative, fond #0a0a0f, texte blanc), la boîte .sdc-scroll-container (100% × 280px, overflow-y: auto, scroll-behavior: smooth), le plateau .sb-clippath-content (colonne flex, gap: 30px, padding: 60px 16px, 900 px de haut), trois .sb-clippath-box (padding: 24px, border-radius: 16px, texte centré, dégradés à 90° de cinq, trois et trois arrêts) avec un h4 de 1,1 rem graisse 800 et un p de 0,75 rem en blanc à 90 %, et l'indice .sdc-scroll-hint « Scrollez ↓ » (absolu, 8 px du bas du cadre, blanc à 30 %, pulsant à 70 % toutes les 2 s). Mesuré à 1 280 px de large : boîtes de 91 px posées à 60, 181 et 302 px du haut du plateau, course 900 − 280 = 620 px. Champ JS vide, aucune variable var(--…), aucune erreur console ; l'unique style en ligne (pile Inter, -apple-system… sur chaque h4) ne charge rien : Inter absente, police système.
Un seul @keyframes sbClipReveal { to { clip-path: inset(0) } }, et chaque bloc le déclare en propriétés longues : animation-name, animation-timeline: view(), animation-range: entry cover 60%, animation-duration: auto, animation-timing-function: linear, animation-fill-mode: both. Le point de départ est le clip-path propre à chaque bloc : inset(0 100% 0 0) (masqué depuis la droite, donc ouverture de gauche à droite), inset(0 0 100%) sur :nth-child(2) (masqué depuis le bas, ouverture de haut en bas), inset(100% 0 0) sur :nth-child(3) (masqué depuis le haut, ouverture du bas vers le haut). Rien d'autre ne bouge : opacité 1 et transform: none relevés à chaque position. view() sans argument prend le conteneur défilant le plus proche, ici la boîte de 280 px, sur l'axe des blocs ; la plage entry → cover 60% démarre quand le haut du bloc touche le bas de la boîte et s'achève à 60 % de sa traversée complète, soit 0,6 × (280 + 91) ≈ 223 px de défilement par bloc — le cache finit de s'ouvrir quand le haut du bloc est à 57 px du haut de la boîte. Tracé linéaire : le balayage horizontal parcourt 1 232 px de largeur en 223 px de molette (5,5 px par pixel défilé), les verticaux 91 px (0,4 px par pixel). inset(0 100%0 0) est écrit sans espace dans le code vendu (reste de minification) : le tokeniseur coupe 100%0 en 100% puis 0, mesuré valide dans Chromium, Firefox et WebKit — écrivez 100% 0 si vous le retouchez.
Mesuré dans Chromium, boîte au repos : le premier bloc, posé à 60 px, a déjà parcouru 220 des 223 px de sa plage — inset(0 1.17% 0 0), 14 px encore masqués à droite ; le deuxième est ouvert à 44 % (inset(0 0 55.5%), 40 px visibles sur 91) ; le troisième, à 302 px, n'est pas encore entré : inset(100% 0 0), invisible. À 60 px de défilement : 100 / 71 / 17 % ; à 120 : 100 / 98 / 44 % ; à 180 : 100 / 100 / 71 % ; à 240 : 98 % pour le troisième ; à 300 px, soit 48 % de la course, les trois sont à inset(0). Remonter referme les caches dans l'autre sens (la progression suit le défilement, pas le temps). De 393 à 620 px, le dernier bloc est sorti par le haut et la boîte n'affiche que son fond : 227 px de course vide, conséquence des 900 px fixés sur le plateau. Le cadre apporte son propre fond sombre, donc l'effet se lit sur une page blanche ; ni id ni script : deux copies sur la même page cohabitent. scroll-behavior: smooth n'agit que sur les positions programmées : scrollTop = 300 se relit 1 puis arrive en 264 ms ; la molette, elle, est instantanée.
Accessibilité
- prefers-reduced-motion absent du code : mesuré sous émulation, mêmes valeurs de
clip-pathà chaque position et indice toujours pulsant. Le balayage est piloté par le lecteur (rien ne bouge sans lui, hormis l'indice) et ne déplace aucun contenu — seule l'arête d'un cache avance —, ce qui le range parmi les animations déclenchées par l'interaction (WCAG 2.3.3), sans composante vestibulaire. Pour montrer tout à plat quand la préférence est active, ajoutez@media (prefers-reduced-motion: reduce) { .sb-clippath-box { animation: none; clip-path: inset(0) } .sdc-scroll-hint { animation: none } }. - Clavier : Chromium et Firefox donnent le focus à la boîte par Tab sans
tabindex(mesuré :activeElement=.sdc-scroll-container) ; WebKit non (focus resté surbody, flèche bas sans effet). Ajouteztabindex="0"et un:focus-visiblevisible sur.sdc-scroll-container. Une fois la boîte focalisée : flèche bas = 40 px (18 % d'un balayage), PageDown = 285 px (les trois blocs ouverts d'un coup), Fin = 620. Au doigt, le geste doit commencer dans la boîte ;overscroll-behavior: containévite d'entraîner la page en bout de course. - Contraste : le
h4blanc de 17,6 px (sous le seuil « grand texte ») vaut 3,7:1 sur le rose, 2,1:1 sur l'ambre, 2,3:1 sur le vert et 2,4:1 sur le cyan du premier dégradé ; seul l'indigo du deuxième atteint 4,5:1 ; lepde 12 px en blanc à 90 % tombe entre 2,0:1 et 3,9:1. Aucun bloc livré n'est conforme sur toute sa largeur : assombrissez les dégradés ou posez le texte sur une bande sombre, comme les trois scènes de cette page. L'indice à 30 % vaut 2,6:1 au repos, de 1,2:1 à 1,8:1 sous la pulsation : l'opacité animée (.3 → .7) se multiplie à celle de la couleur. - Lecteurs d'écran et repli :
clip-pathne retire rien de l'arbre d'accessibilité — les trois blocs sont lus en entier, masqués ou non, et rien n'est à annoncer. Troish4sans h2/h3 au-dessus cassent le plan de la page : prenez le niveau qui suit votre titre courant. L'indice « Scrollez ↓ » est en français quelle que soit la langue de la page : posezaria-hidden="true"dessus ou retirez-le. Là oùanimation-timelinen'existe pas (Firefox, mesuré), les trois blocs sont àinset(0)dès le chargement : tout est lisible, à plat, tant queanimation-fill-mode: bothreste en place (voir « Mode de remplissage »).
Compatibilité navigateur
CSS seul, zéro JavaScript, zéro dépendance. Trois propriétés récentes : animation-timeline: view(), animation-range et animation-duration: auto — Chrome et Edge 115, Safari 26 et iOS 26. Le reste est ancien : clip-path: inset(), flexbox, dégradés, scroll-behavior, ::-webkit-scrollbar (Blink et WebKit seulement, Firefox garde sa barre native). Firefox : aucune version ne rend les animations pilotées au défilement. Mesuré sur le code vendu, page vierge : CSS.supports('animation-timeline: view()') faux, et les trois blocs affichés en entier dès le repos (inset(0)), avant tout défilement — la boîte défile, l'indice pulse, aucun cache ne s'ouvre : le visiteur Firefox voit trois cartes colorées, à plat, complètes et lisibles. Pas de repli @supports dans le code : il n'en a pas besoin, l'état final est le contenu entier.
Sans animation-timeline (Firefox, Chrome et Edge avant 115, Safari avant 26) : animation-duration: auto est ignoré, la durée retombe à 0 s, l'animation est finie avant le premier rendu et animation-fill-mode: both laisse la dernière image, inset(0) — blocs entiers, sans balayage, boîte qui défile comme une zone ordinaire. Sans ::-webkit-scrollbar, barre du système ; sans scroll-behavior, défilements programmés sans lissage. Aucun script : rien à perdre.
Le code
Structure HTML à coller dans votre page (le CSS + JS sont inclus avec Premium) :
<div class="demo-preview">
<div class="sdc-scroll-container">
<div class="sb-clippath-content">
<div class="sb-clippath-box">
<h4>…</h4>
<p>…</p>
</div>
<div class="sb-clippath-box">
<h4>…</h4>
<p>…</p>
</div>
<div class="sb-clippath-box">
<h4>…</h4>
<p>…</p>
</div>
</div>
</div>
<span class="sdc-scroll-hint">…</span>
</div>
Le code complet HTML + CSS + JS de Clip Path Reveal, prêt à coller — et les 811 effets. Ou 299 € une fois, accès à vie.
Personnaliser
Options passées à l'API ou attributs data-* :
| Option / propriété | Défaut | Effet |
|---|---|---|
Direction d'ouverture (CSS — clip-path de départ sur .sb-clippath-box, :nth-child(2), :nth-child(3)) |
inset(0 100% 0 0) / inset(0 0 100%) / inset(100% 0 0) | Le côté à 100 % est celui qui masque : inset(0 0 0 100%) ouvre de droite à gauche (4e ligne de la scène e-sport), inset(0 50%) ouvre depuis le centre vers les deux bords, inset(50% 0) depuis la ligne médiane. Un round 16px après les valeurs garde les coins arrondis pendant le balayage ; la fin, inset(0), est commune aux trois. |
Plage (CSS — animation-range: entry cover 60%) |
entry → cover 60 % (223 px par bloc) | Longueur du balayage = 60 % de la traversée, soit 0,6 × (hauteur de la boîte + hauteur du bloc). entry seul (= entry 0% entry 100%) finit quand le bloc est tout juste entré : 91 px ici, 2,5 fois plus vif. entry 20% cover 60% retarde le départ, ce qui évite qu'un bloc placé haut dans le plateau soit déjà ouvert au repos. |
Timeline (CSS — animation-timeline: view()) |
view() — conteneur défilant le plus proche, axe des blocs | view(inline) suit un défilement horizontal ; view(block 20% 0) rétrécit la zone de visibilité et retarde l'entrée ; scroll() lie les trois blocs à la progression de toute la boîte et les ouvre ensemble. Attention : tout ancêtre en overflow: hidden devient la référence de view() et fige l'animation (mesuré dans la scène du théâtre). |
Hauteurs (CSS — .sdc-scroll-container { height: 280px }, .sb-clippath-content { height: 900px; padding: 60px 16px; gap: 30px }) |
280 px / 900 px (course 620 px, dont 227 vides) | La hauteur de la boîte entre dans la longueur du balayage ; celle du plateau fait la course. À height: auto, le plateau mesure 453 px et la course 173 : le dernier bloc ne s'ouvre plus qu'à 88 % — gardez un padding-bottom d'au moins 280 px ou du contenu sous les blocs. |
Tracé (CSS — animation-timing-function: linear) |
linear | Avec une timeline de défilement, la fonction transforme la position en progression : ease-out ouvre vite puis freine, ease-in l'inverse, steps(8) découpe le balayage en huit crans. Le rythme reste dicté par la main du lecteur. |
Mode de remplissage (CSS — animation-fill-mode: both) |
both | Indispensable des deux côtés : dans un navigateur qui comprend la timeline, none rendrait au bloc son clip-path de départ une fois la plage dépassée — il disparaîtrait après s'être ouvert ; dans un navigateur qui ne la comprend pas, c'est both qui tient l'image finale inset(0) (mesuré dans Firefox). forwards suffit pour les deux cas. |
Indice « Scrollez ↓ » (CSS — .sdc-scroll-hint, @keyframes sdcHintPulse) |
8 px du bas du cadre, blanc à 30 %, pulsation 2 s | Français en dur, absolu par rapport à .demo-preview — sans ce cadre en position: relative, il tombe au bas de la page. Pour le retirer, supprimez le span et la @keyframes ; pour le garder, traduisez-le et posez aria-hidden="true". |
FAQ
animation-timeline: view() et animation-duration: auto, la durée retombe à 0 s et animation-fill-mode: both conserve la dernière image, inset(0) : les trois cartes sont affichées en entier dès le chargement, la boîte défile, l'indice pulse. Rien n'est perdu ni caché, seul le balayage manque — d'où l'absence de repli @supports. Si le balayage vous est indispensable partout, il faut le piloter en JavaScript (un IntersectionObserver ou un écouteur scroll qui écrit clip-path), ce que cet effet ne fait pas ; sinon, acceptez un rendu à plat pour 3 à 4 % des visiteurs.padding-top), il a donc déjà parcouru 220 de ses 223 px au repos — inset(0 1.17% 0 0), 14 px encore masqués. Le troisième est à 302 px, sous le bord de la boîte : pas entré, masqué à 100 %, complet seulement à 300 px de défilement, alors que la course en fait 620 dont 227 vides. Pour que le premier bloc balaie lui aussi sous les yeux du lecteur, posez-le sous la ligne de flottaison (padding-top: 280px) ou retardez le départ avec animation-range: entry 20% cover 60% ; pour supprimer la course vide, passez le plateau en height: auto avec un padding-bottom suffisant.inset(0 100% 0 0) à inset(0) par une animation temporelle : animation: 1s ease 0s 1 normal forwards clipRevealAnim — elle se joue une fois, au chargement, en une seconde, quelle que soit la position de défilement, et fonctionne partout, Firefox compris. fx-0553 remplace le temps par la position : animation-timeline: view() lie chaque bloc à son entrée dans la boîte, la progression suit la molette dans les deux sens, et trois directions d'ouverture coexistent (droite, haut, bas) — au prix de Chrome 115 / Safari 26 comme plancher et d'un rendu à plat dans Firefox. Choisissez selon le déclencheur : un titre qui doit se révéler à l'arrivée sur la page, fx-0652 ; des blocs qui doivent se révéler quand le lecteur les atteint, fx-0553.