Mixed Content Snap
Une fenêtre de 280 px en scroll-snap-type: y — proximité, pas mandatory — et cinq sections de 280, 140, 140, 80 et 280 px alignées en start : points d'accroche à 0, 280, 420, 560 et 640 px, une section plus courte que la fenêtre ne l'occupe jamais seule, et un cran de molette depuis le haut s'arrête à 100 px, entre deux. Zéro JavaScript.
Vous aurez accès à la démo interactive avec un compte gratuit.
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 est un .demo-preview (flex centré, min-height: 300px, position: relative, fond #0a0a0f) qui contient .sn-mixed-container — width: 100%, height: 280px, overflow-y: auto, scroll-snap-type: y, scroll-behavior: smooth — et, à côté, l'indice .sdc-scroll-hint « Scrollez ↓ » : absolu à 8 px du bas du .demo-preview, 10,4 px, blanc à 30 %, opacité pulsée de .3 à .7 en 2 s (sdcHintPulse). Dans le conteneur, cinq .sn-mixed-section en scroll-snap-align: start, colonne flex centrée, padding: 16px, dont la hauteur vient d'une seconde classe : .sn-full 280 px (la fenêtre exactement), .sn-half 140 px, .sn-quarter 80 px. L'ordre vendu — pleine, demi, demi, quart, pleine — fait 920 px de contenu pour 640 px de course. Les fonds sont cinq dégradés à 135° attribués par :nth-child(1) à (5), de #1e1b4b à #8b5cf6 : ils suivent le rang, pas la taille. Textes : h4 .85 rem (13,6 px) blanc, p .68 rem (10,9 px) blanc à 60 % ; chaque h4 porte en ligne font-family: Inter, …, un reste du catalogue.
Les points d'accroche sont les hauts de section : 0, 280, 420, 560 et 640 px — le dernier coïncide avec la fin de course parce que la section finale fait la hauteur de la fenêtre. C'est là que les hauteurs mixtes changent la lecture : une section plus courte que la fenêtre ne la remplit jamais seule. À 280, les deux demi-sections se partagent l'écran ; à 420, la seconde demi, le quart et 60 px de la finale ; à 560, le quart occupe le haut et la finale les 200 px restants. Le rythme, ce sont les écarts entre arrêts : 280, 140, 140, 80. Et scroll-snap-type: y sans second mot-clé vaut proximity, pas mandatory : l'accroche ne joue qu'à portée d'un point. Mesuré dans Chromium 151 : une position laissée à 93 px d'un point y est ramenée, à 94 px elle reste — le seuil est un tiers de la fenêtre (280 / 3). Conséquence : un cran de molette (100 px) depuis le haut s'arrête à 100 px et y reste (encore à 100 px deux secondes plus tard) ; deux crans en 120 ms atteignent 280 ; ensuite chaque cran retombe à portée d'un point : 280 → 420 → 560 → 640. Au trackpad, 5 × 20 px sont ramenés à 0, 13 × 20 px s'arrêtent à 143 px, entre deux.
scroll-behavior: smooth ne concerne que les défilements programmés (scrollTo, ancres, touches) : mesuré 0 → 640 px en 395 ms, 226 px parcourus à 100 ms. Aucun JavaScript : pas d'écouteur, rien à retirer, l'animation d'accroche est celle du navigateur. Trois limites tiennent au code lui-même. Les fonds par :nth-child : une sixième section n'en a aucun (mesuré background: none). La dernière section : passée en .sn-half, son haut (640) dépasse la course (500) et elle ne s'aligne jamais en haut (mesuré : arrêt à 500). L'indice : il chevauche les 10 derniers pixels du conteneur (280–292 px contre 10–290) et, sans le .demo-preview positionné, file au bas de la fenêtre (mesuré à 479 px pour un conteneur fini à 280). Aucune variable CSS du site, aucune erreur console.
Accessibilité
- prefers-reduced-motion absent : sous
reduce,scroll-behavior: smoothreste actif (mesuré : 23 px parcourus 50 ms après unscrollTo) et la pulsation de l'indice tourne (playState: running). Le défilement est à l'initiative du lecteur et l'animation d'accroche est celle du navigateur, mais ajoutez@media (prefers-reduced-motion: reduce) { .sn-mixed-container { scroll-behavior: auto } .sdc-scroll-hint { animation: none } }. Point positif du mode proximité : aucun contenu n'est inatteignable, contrairement àmandatoryqui peut retenir le lecteur entre deux points. - Clavier : le conteneur est un
divsanstabindex. Les Chromium récents et Firefox rendent les zones défilantes sans enfant focalisable atteignables au clavier (mesuré dans Chromium 151 : Tab s'y arrête, flèche bas 40 px par appui sans accroche — 40, 80, 120, 160 —, PageDown 420, Espace 280, End 640), Safari non. Poseztabindex="0",role="region"et unaria-labelsur.sn-mixed-container. - Contraste mesuré sur les pixels des captures :
h4blanc de 14,0:1 (section 1) à 5,1:1 (section 5) — conforme ;pblanc à 60 % : 6,0 · 5,1 · 4,2 · 3,3 · 2,8:1 — les sections 3 à 5 sont sous 4,5:1, et à 10,9 px le texte n'est pas « large ». Passezrgba(255, 255, 255, .6)à.85(≥ 4,5:1 partout) ou éclaircissez moins les derniers dégradés. L'indice (10,4 px, 30 % de blanc) mesure 2,6:1 sur#0a0a0fet 2,5:1 sur la section qu'il recouvre : décoratif, mettez-learia-hidden="true"— et traduisez « Scrollez ↓ », codé en dur. - Tailles et structure : 13,6 px pour les titres, 10,9 px pour les paragraphes — ce sont les tailles d'une carte de catalogue, remontez-les à 14–16 px en page. Les titres sont des
h4sansh1–h3au-dessus : adaptez le niveau à votre plan. Les sections sont desdiv: pour un lecteur d'écran, le rythme des hauteurs n'existe pas, la hiérarchie doit aussi être dans les titres. Au doigt, l'accroche de proximité fonctionne et le conteneur de 280 px laisse le geste continuer sur la page en fin de course (overscroll-behaviorpar défaut) : pas de piège au défilement.
Compatibilité navigateur
CSS seul : scroll-snap-type / scroll-snap-align (syntaxe moderne), overflow-y: auto, linear-gradient, @keyframes ; scroll-behavior: smooth en confort. Aucun script, aucune bibliothèque, aucune variable CSS. C'est du CSS classique, pas une animation pilotée par animation-timeline : Firefox le rend complètement, alors qu'il ne prend en charge les animations au défilement natives dans aucune version.
Sans scroll-snap (navigateurs antérieurs), le conteneur est une simple boîte défilante : les cinq sections gardent leurs hauteurs et leurs fonds, tout reste lisible, rien n'accroche. Sans scroll-behavior (Safari avant 15.4), les sauts programmés sont instantanés ; le défilement au doigt ou à la molette n'en dépend pas. Sans JavaScript : identique, il n'y en a pas.
Le code
Structure HTML à coller dans votre page (CSS + JS disponibles avec un compte premium) :
<div class="demo-preview">
<div class="sn-mixed-container">
<div class="sn-mixed-section sn-full">
<h4>Section Principale</h4>
<p>Hauteur complete</p>
</div>
<div class="sn-mixed-section sn-half">
<h4>Section Moyenne</h4>
<p>Demi-hauteur</p>
</div>
<div class="sn-mixed-section sn-half">
<h4>Section Moyenne</h4>
<p>Demi-hauteur</p>
</div>
<div class="sn-mixed-section sn-quarter">
<h4>Petite</h4>
<p>Compacte</p>
</div>
<div class="sn-mixed-section sn-full">
<h4>Section Finale</h4>
<p>Retour hauteur complete</p>
</div>
</div>
<span class="sdc-scroll-hint">Scrollez ↓</span>
</div>
HTML + CSS + JS complet, prêt à coller — avec des centaines d'effets premium.
Personnaliser
Options passées à l'API ou attributs data-* :
| Option / propriété | Défaut | Effet |
|---|---|---|
Mode d'accroche (CSS — .sn-mixed-container scroll-snap-type) |
y (= y proximity) |
En proximité, l'accroche ne joue qu'à moins d'un tiers de fenêtre d'un point (93 px) : un cran de molette depuis le haut reste à 100 px. y mandatory ramène toujours au point le plus proche — sans risque ici, aucune section ne dépasse la fenêtre. |
Hauteur de la fenêtre (CSS — .sn-mixed-container et .sn-full) |
280 px / 280 px | Les deux vont ensemble : une pleine section doit faire exactement la fenêtre pour l'occuper seule, et le seuil de proximité suit (un tiers). En page, 100vh pour les deux ; dans un composant, une valeur fixe. |
Paliers (CSS — .sn-half, .sn-quarter) |
140 px / 80 px | Ce sont les écarts entre arrêts. Ajoutez des classes (.sn-third à 93 px). Un écart inférieur au seuil enchaîne deux arrêts en un cran ; un écart de 280 laisse une zone morte de 94 à 186 px où un cran s'arrête entre deux. |
Alignement (CSS — .sn-mixed-section scroll-snap-align) |
start |
center centre une section courte dans la fenêtre, ses voisines de part et d'autre ; end sur la dernière section la rend atteignable même plus courte que la fenêtre (son bas s'aligne sur le bas du cadre). |
| Ordre et dernière section (HTML) | pleine · demi · demi · quart · pleine | Le dernier point d'accroche doit rester ≤ la course (contenu − 280). Terminez par une .sn-full, ou donnez à la dernière section scroll-snap-align: end, ou ajoutez un padding-bottom au conteneur. |
Fonds (CSS — .sn-mixed-section:nth-child(n)) |
5 dégradés #1e1b4b → #8b5cf6, par rang | Liés à la position, pas à la classe de taille : la sixième section n'a pas de fond. Portez-les sur .sn-full / .sn-half / .sn-quarter pour que la couleur dise la hauteur. |
Lissage (CSS — scroll-behavior) |
smooth |
Anime scrollTo, les ancres et les touches (395 ms pour 640 px mesurés), pas la molette ni le doigt. Passez à auto sous prefers-reduced-motion. |
Indice (HTML/CSS — .sdc-scroll-hint) |
« Scrollez ↓ », 8 px du bas, pulsation 2 s | Exige un parent en position: relative et chevauche 10 px du conteneur. Remontez-le (bottom: 18px), traduisez-le, ou supprimez-le avec sa @keyframes. |
FAQ
scroll-snap-type: y sans second mot-clé signifie proximity : le navigateur n'accroche que si le défilement s'arrête à portée d'un point — dans Chromium, un tiers de la fenêtre, soit 93 px ici (mesuré : 93 px ramenés, 94 px laissés). Un cran de molette fait 100 px : depuis le haut, il s'arrête à 100 px, à 100 px du point 0 et à 180 px du point 280, hors de portée des deux. Le second cran atteint 200 px, à 80 px du point 280, et l'accroche joue ; ensuite chaque cran retombe à portée (280 → 420 → 560 → 640). Pour un arrêt net à chaque cran, écrivez scroll-snap-type: y mandatory — ici aucune section ne dépasse la fenêtre, rien ne devient inatteignable. Garder la proximité est aussi un choix : le lecteur peut s'arrêter au milieu d'une section pleine.y mandatory — chaque arrêt montre exactement une section, les points sont tous à 280 px d'écart. Ici les points sont à 280, 140, 140 et 80 px d'écart, le mode est la proximité, et trois arrêts sur cinq montrent plusieurs sections à la fois : la hauteur devient un signal de poids. Snap + Progress ajoute une barre et des points de navigation en JavaScript, mais son script calcule la section courante par Math.round(scrollTop / clientHeight) — il suppose des sections égales. Branché sur ces hauteurs, il donnerait l'index 1 aux arrêts 280 et 420 et l'index 2 aux arrêts 560 et 640 : deux points sur cinq jamais allumés. Pour une navigation ici, comparez scrollTop aux offsetTop réels des sections.scrollHeight − clientHeight. Avec la dernière section en .sn-half, le contenu fait 780 px, la course 500, et le haut de cette section est à 640 : son point d'accroche est au-delà de la fin (mesuré : arrêt à 500, la section reste collée en bas du cadre, sous le quart). Le code vendu évite le problème en terminant par une .sn-full. Trois issues si votre dernière section doit être courte : scroll-snap-align: end sur elle (son bas s'aligne sur le bas du cadre — le réglage des trois exemples), un padding-bottom sur le conteneur égal à la différence (140 px pour une demi), ou une section vide de fin.