Split Screen Sticky
Visuel épinglé à 10 px dont l'icône SVG tourne de 360° sur les 720 px de course — scroll(), 0,5° par pixel, réversible avec le geste — pendant que trois blocs se révèlent par view(). CSS seul, 2,7 Ko.
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 s'emboîte en cinq niveaux : 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, barre de 4 px par ::-webkit-scrollbar), le plateau .ss-split-content (1 000 px, padding: 12px, colonne flex), le visuel .ss-split-visual (pleine largeur × 100 px, dégradé #1a1a3e → #2d1b69, position: sticky; top: 10px; z-index: 2) avec son SVG de 60 px en trait blanc, puis la pile .ss-split-items et ses trois .ss-split-item de 120 px, posés de 128 à 520 px. Sous la boîte, l'indice .sdc-scroll-hint « Scrollez ↓ » pulse entre .3 et .7 toutes les 2 s. Course : 1000 − 280 = 720 px. CSS de 2,7 Ko, champ JavaScript vide, aucune variable du site, aucune ressource externe, aucune erreur console ; seul reste étranger, un style="font-family: Inter…" en ligne sur les trois h4.
La rotation tient en une règle : .ss-split-visual svg { animation-name: ssRotateSvg; animation-timeline: scroll(); animation-range: normal; animation-duration: auto; animation-timing-function: linear; animation-fill-mode: none }, avec @keyframes ssRotateSvg { to { transform: rotate(360deg) } }. scroll() sans argument prend le défileur le plus proche — la boîte de 280 px, pas la page — et normal étale l'animation sur toute sa course : progression = scrollTop ÷ 720, soit 0,5° par pixel. Mesuré dans Chromium 151 par crans de molette de 120 px : 60°, 120°, 180°, 240°, 300°, puis transform: none à 720 px, ce qui vaut 360°. Trois crans en arrière ramènent 180°, puis 0° : rien ne tourne sans le lecteur, et l'icône revient sur ses pas avec lui. Le sticky; top: 10px tient le visuel à 12 px du haut de la boîte au repos (le padding du plateau), à 10 px dès le premier pixel, et jusqu'au bout — son bloc conteneur est le plateau entier.
Les blocs suivent une seconde timeline : .ss-split-item { animation-name: sbFadeUp; animation-timeline: view(); animation-range: entry entry 50%; animation-fill-mode: both }, de opacity: 0; transform: translateY(20px) vers 1 et translateY(0). view() suit la position du bloc lui-même dans la boîte : le fondu commence quand son haut touche le bord bas et finit quand la moitié du bloc (60 px) est entrée. Au repos, le bloc 1 (128 → 248 px) est entier dans la boîte : opacité 1. Le bloc 2 commence à 264 px, 16 px avant le bord : 13 % entré, 27 % de sa plage — opacité .27, décalé de 14,7 px, mesuré ; il atteint 1 à 44 px de défilement. Le bloc 3 (400 px) reste à 0 jusqu'à 120 px et vaut 1 dès 180. Chaque bloc qui monte passe sous le visuel (z-index: 2). À 520 px, le dernier bloc a quitté la boîte : les 200 derniers pixels de course (28 %, 100° de rotation) ne montrent que le visuel épinglé sur le fond vide.
Accessibilité
- prefers-reduced-motion absent du code : mesuré sous émulation dans Chromium, rien ne change — 60° à 120 px, bloc 2 à .27, indice pulsant,
scroll-behavior: smooth. Le mouvement est piloté par le lecteur (animation déclenchée par l'interaction, WCAG 2.3.3) et rien n'est caché, mais ajoutez@media (prefers-reduced-motion: reduce) { .ss-split-visual svg, .ss-split-item, .sdc-scroll-hint { animation: none } .ss-split-item { opacity: 1; transform: none } .sdc-scroll-container { scroll-behavior: auto } }—opacityettransformsont indispensables :animation: noneseul laisse les blocs invisibles. - Clavier : mesuré, Tab donne le focus à la boîte dans Chromium et Firefox sans
tabindex— flèche bas avance de 40 px, soit 20° (51 px dans Firefox), PageDown de 245 px, Fin va à 720 ; WebKit ne la focalise pas (focus resté surbody). Ajouteztabindex="0", unaria-labelet un style:focus-visible. Au doigt, le défilement est natif (aucun écouteur) ;overscroll-behavior: containévite d'entraîner la page en bout de course. - Contraste sur les blocs (blanc à 4 % sur
#0a0a0f) : titres 18,4:1 ; paragraphes à blanc 50 % 5,3:1 en 11,5 px ; listes à blanc 40 % 3,8:1, sous le seuil 4,5:1 — passez-les àrgba(255,255,255,.55). L'indice « Scrollez ↓ » est en blanc à 30 % et son opacité pulse de .3 à .7 — les deux se multiplient : 2,6:1 au repos, de 1,2:1 à 1,8:1 sous la pulsation, en 10,4 px ; décoratif, posez-luiaria-hidden="true". Un bloc posé n'est jamais semi-transparent : le fondu ne concerne que son entrée. - Lecteurs d'écran et cadre : le SVG n'a ni
<title>niaria-hidden— donnez-lui l'un ou l'autre selon qu'il porte un sens. Les blocs àopacity: 0restent lus dans l'ordre. Troish4sans h2/h3 au-dessus : prenez le niveau qui suit votre titre. Les textes de démonstration sont en français, accents manquants (« Renforcee », « Protegez vos donnees »). Collez le HTML avec son cadre : sans.demo-preview, mesuré, la boîte n'a plus de fond — texte blanc sur page blanche — et l'indice tombe au bas de la fenêtre.
Compatibilité navigateur
CSS seul : animation-timeline: scroll() et view(), animation-range, animation-duration: auto — arrivés ensemble dans Chrome et Edge 115, Safari 26 (iOS 26). Firefox : aucune version (module derrière layout.css.scroll-driven-animations.enabled, désactivé par défaut). Le reste est classique : position: sticky, flexbox, dégradé, @keyframes, scroll-behavior. Zéro dépendance, zéro JavaScript. Mesuré dans WebKit 26.5 : mêmes valeurs que Chromium (60° à 120 px, bloc 2 à .27, 360° à 720).
Mesuré dans Firefox 153, code vendu, page vierge : CSS.supports('animation-timeline: scroll()') répond faux, animation-duration: auto est rejeté et se calcule 0s — les deux animations retombent sur l'horloge du document et durent 0 s. Le SVG garde transform: none (fill-mode: none) : l'icône ne tourne pas, à aucune position. Les blocs prennent leur image finale (fill-mode: both) : opacité 1, translateY(0) dès le chargement — les trois se lisent à plat, le troisième compris, avant tout défilement. Le visuel reste épinglé, la boîte défile avec sa barre native. Aucun repli @supports dans le code, et rien n'y est à rattraper : l'état final des keyframes est l'état lisible. Pour l'écrire noir sur blanc : @supports not (animation-timeline: scroll()) { .ss-split-item { animation: none; opacity: 1; transform: none } }. Même rendu dans Chrome avant 115 et Safari avant 26.
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="ss-split-content">
<div class="ss-split-visual">
<svg>…</svg>
</div>
<div class="ss-split-items">
<div class="ss-split-item">
<h4>…</h4>
<p>…</p>
<ul><li>…</li><li>…</li><li>…</li></ul>
</div>
<div class="ss-split-item">…</div>
<div class="ss-split-item">…</div>
</div>
</div>
</div>
<span class="sdc-scroll-hint">…</span>
</div>
Le code complet HTML + CSS + JS de Split Screen Sticky, 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 |
|---|---|---|
Course (CSS — .ss-split-content { height: 1000px }) |
1 000 px (course 720, 0,5° par pixel) | Course = plateau − boîte, et la rotation s'y étale toujours en entier. 570 px → course 290, 1,24° par pixel, texte et rotation finissent ensemble (dernier bloc calé sous le visuel, mesuré). height: auto avec vos blocs : la course suit le contenu. |
Hauteur de la boîte (CSS — .sdc-scroll-container { height: 280px }) |
280 px | À 400 px (mesuré) : course 600, blocs 1 et 2 posés dès le chargement, bloc 3 à 0. Retirez height et overflow-y pour laisser la page défiler : scroll() et view() remontent alors à la fenêtre. |
Angle (CSS — @keyframes ssRotateSvg { to { transform: rotate(360deg) } }) |
360° (un tour sur la course) | 720deg deux tours, -360deg sens inverse, 180deg un demi-tour — mesuré : l'icône reste à 180° en bout de course malgré fill-mode: none. Ajoutez scale(1.2) dans la même image pour tourner et grossir ensemble. |
Plage du fondu (CSS — .ss-split-item { animation-range: entry entry 50% }) |
entry 0 % → entry 50 % (60 des 120 px) | entry entry 100% étale le fondu sur toute l'entrée (bloc 2 à .13 au repos au lieu de .27, mesuré) ; entry 0% entry 30% le resserre (.44). Gardez une fin avant entry 100% : le bloc doit être net avant que l'œil arrive dessus. |
Amplitude du fondu (CSS — .ss-split-item { opacity: 0; transform: translateY(20px) }) |
0 → 1, 20 px de montée | Le départ est dans la règle, l'arrivée dans @keyframes sbFadeUp. translateY(40px) accentue la montée ; translateY(0) scale(.96) la remplace par un léger grossissement. |
Épinglage (CSS — .ss-split-visual { top: 10px; height: 100px; z-index: 2 }) |
10 px du haut, 100 px de haut, au-dessus des blocs | top: 0 colle le visuel au bord ; height décale le premier bloc (12 px + hauteur + 16 px) ; sans z-index: 2, les blocs qui montent passeraient par-dessus le visuel au lieu de glisser dessous. |
Visuel (HTML — <svg width="60" height="60"> ; CSS — .ss-split-visual svg { stroke: #fff; stroke-width: 1.5 }) |
60 px, trait blanc de 1,5, trois couches | Remplacez le contenu du <svg> (logo, pictogramme) : tout svg du visuel tourne, un <img> non — élargissez le sélecteur (.ss-split-visual svg, .ss-split-visual img). Un texte posé à côté du SVG dans le visuel reste fixe : les trois scènes de cette page le font. |
FAQ
view() mesure l'entrée de chaque bloc dans la boîte de 280 px et que entry entry 50% termine le fondu quand 60 px sont passés. Au chargement, le bloc 2 commence à 264 px : 16 px déjà entrés, 13 % de sa hauteur, 27 % de la plage — opacité .27, mesurée. Le bloc 3 commence à 400 px, 120 px sous le bord : 0 jusqu'à ce que le défilement l'y amène, 1 à 180 px. C'est la géométrie du code vendu, rien n'est cassé. Pour un repos net : une boîte de 400 px (blocs 1 et 2 à 1, mesuré), une plage plus courte (entry 0% entry 30% : .44), ou padding-top: 32px sur .ss-split-items — le bloc 2 démarre alors à 280 px, tout juste hors de la boîte, à 0..ss-split-content { height: 570px } — course 290, 1,24° par pixel, le troisième bloc s'arrête calé sous le visuel (110 → 230 px, mesuré) ; height: auto avec assez de blocs pour que la course vienne du contenu (les scènes de cette page : 516 px pour cinq blocs, 376 pour quatre) ; ou garder 1 000 px et ajouter trois blocs — en bout de course, le sixième s'arrête à 88 px, son haut glissé de 22 px sous le visuel (mesuré)..sdc-scroll-container de 280 px, bandeau sticky à 10 px, panneaux en view() sur entry 0% entry 50% — mais pas la timeline du haut. Dans fx-0585 (gratuit), ssImgChange sur scroll(nearest) anime le fond du bandeau par paliers : quatre dégradés tenus sur 0–20 %, 25–45 %, 50–70 % et 75–100 % de la course de 920 px, 5 % de transition entre deux — quatre états discrets, un « 01 » fixe, pas d'image. Ici la timeline anime une rotation continue de l'illustration SVG : 0,5° par pixel, chaque position a son angle, et la marche arrière la défait degré par degré. fx-0585 pour signaler des sections (une couleur = une étape) ; fx-0589 pour un objet que le lecteur manipule du geste — icône, engrenage, pictogramme — et qui doit revenir avec lui.