Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Scrolling

The browser scrolls by itself, but CSS can tune it: make a jump to an anchor smooth, stop a carousel exactly on a slide, keep scrolling from "spilling" onto the page. In the examples it is not the window that scrolls but a block with overflow: auto from the overflow lesson — the properties work the same way.

Smooth scrolling

A link like <a href="#prices"> from the links lesson jumps to the target instantly. scroll-behavior: smooth makes this jump smooth. In the example, buttons replace the links: they do the same jump from JavaScript, and container: 'nearest' asks to scroll only the nearest block, not the whole lesson page:

<style>
  .box { height: 140px; overflow: auto; scroll-behavior: smooth; border: 1px solid silver; }
  .box p { height: 120px; margin: 0; padding: 8px; }
</style>
 
<button onclick="document.querySelector('#end').scrollIntoView({ container: 'nearest' })">Down</button>
<button onclick="document.querySelector('#start').scrollIntoView({ container: 'nearest' })">Up</button>
<div class="box">
  <p id="start">Start</p>
  <p>Middle</p>
  <p id="end">End</p>
</div>

For the whole page the rule is written like this: html { scroll-behavior: smooth; }.

An anchor under a sticky header

If the header sticks to the top (position: sticky or fixed), a jump to an anchor hides the section heading under it. scroll-margin-top leaves space above the target:

<style>
  .page { height: 160px; overflow: auto; border: 1px solid silver; }
  .page header { position: sticky; top: 0; padding: 8px; background: steelblue; color: white; }
  .page section { height: 150px; padding: 0 8px; scroll-margin-top: 44px; }
</style>
 
<button onclick="document.querySelector('#delivery').scrollIntoView({ container: 'nearest' })">To delivery</button>
<div class="page">
  <header>Shop</header>
  <section><h3>Catalog</h3></section>
  <section id="delivery"><h3>Delivery</h3></section>
  <section><h3>Contacts</h3></section>
</div>

The margin equals the header height plus a little extra. Remove scroll-margin-top, and the "Delivery" heading ends up under the header.

Scroll snapping stops scrolling exactly on an element instead of anywhere. The container gets scroll-snap-type, the items get a snap point with scroll-snap-align. Scroll the strip sideways:

<style>
  .carousel { display: flex; gap: 8px; overflow-x: auto; scroll-snap-type: x mandatory; }
  .slide { flex: 0 0 80%; height: 100px; display: grid; place-items: center; scroll-snap-align: center; color: white; font-size: 24px; }
</style>
 
<div class="carousel">
  <div class="slide" style="background: tomato;">1</div>
  <div class="slide" style="background: seagreen;">2</div>
  <div class="slide" style="background: steelblue;">3</div>
</div>

Wherever you let go of the strip, it slides on to the nearest slide and centres it.

Snap values

PropertyValueWhat it does
scroll-snap-typex or ythe axis snapping works on
mandatoryalways stop on an element
proximityonly if scrolling stopped close to one
scroll-snap-alignstart, center, endwhich edge of the element to align

overscroll-behavior

When a nested block is scrolled to the end, further wheel movements start scrolling the page. This is called scroll chaining. overscroll-behavior: contain stops it at the edge of the block:

<style>
  .lists { display: flex; gap: 8px; }
  .list { flex: 1; height: 110px; overflow: auto; border: 1px solid silver; }
  .list p { margin: 0; padding: 6px 8px; }
  .contain { overscroll-behavior: contain; }
</style>
 
<div class="lists">
  <div class="list"><p>A regular list</p><p>Anna</p><p>Ben</p><p>Clara</p><p>David</p><p>Emma</p></div>
  <div class="list contain"><p>contain</p><p>Anna</p><p>Ben</p><p>Clara</p><p>David</p><p>Emma</p></div>
</div>

Scroll each list to the end with the wheel and keep going: in the left one the lesson page starts scrolling, in the right one it does not. This is used for dropdown menus, chats and modal windows.

Common mistake

scroll-snap-type is given to the items instead of the container — and snapping does not work. The snap type belongs to the element that scrolls, while the items get only scroll-snap-align:

<style>
  .carousel { display: flex; gap: 8px; overflow-x: auto; }
  .slide { flex: 0 0 80%; height: 80px; background: gold; scroll-snap-type: x mandatory; scroll-snap-align: start; }
</style>
 
<div class="carousel">
  <div class="slide">The strip</div>
  <div class="slide">stops</div>
  <div class="slide">anywhere</div>
</div>

Thanks for reading!

Donate to the site — we work hard for you.