oh snap!

That Scroll Control

Overflow

A Review

Clipping

.scroll-x {
  overflow-x: hidden;
}
.scroll-y {
  overflow-y: hidden;
}

Horizontal

.scroll-x {
  overflow-x: auto;
}
.scroll-x-rtl {
  overflow-x: auto;
  direction: rtl;
}

Vertical

.scroll-y {
  overflow-y: auto;
}
.scroll-y-rtl {
  overflow-y: auto;
  direction: rtl;
}

Logical Axis

.scroll-block {
  overflow-block: auto;
}
.scroll-inline {
  overflow-inline: auto;
}

Both

.scroll {
  overflow: auto;
}
.scroll-rtl {
  overflow: auto;
  direction: rtl;
}

Glossary

Root Scroller

Top most scroller. Typically the HTML tag. Scroller promotion possible.

Implicit Scroller

A nested scroller or any scroller that's not the root scroller lol

ScrollPort

The viewport frame of a scroller

Scroll Axis

A 2D coordinate system to translate along

Scroll Behavior

From CSS or JS, control if browser scroll should be instant or animated

Overscroll Behavior

Should a nested scroller trap it's inertia or not

Overscroll Effect

UI feedback informing users they're at the beginning or end of a scroller

Scroll Hint

UI to help users see there's room to scroll

Scroll Snap

The Basics

Syntax

/* for the scrollport */
scroll-snap-type: x proximity;
scroll-snap-type: y mandatory;

/* for scroll items */
scroll-snap-align: start;
scroll-snap-align: center;
scroll-snap-align: end;

Chrome DevTools

Elements > "scroll-snap" badge

Horizontal

.snap-x {
  scroll-snap-type: x mandatory;

  & > * {
    scroll-snap-align: center;
  }
}

Vertical

.snap-y {
  scroll-snap-type: y proximity;

  & > * {
    scroll-snap-align: center;
  }
}

Matrix

.snap-both-axis {
  scroll-snap-type: both mandatory;
}

.snap-both-axis > * {
  scroll-snap-align: center;
}

Snap Stop

.snap-y > * {
  scroll-snap-stop: always;
}

Matrix Stops

.snap-both-axis {
  scroll-snap-type: both mandatory;
}

.snap-both-axis > * {
  scroll-snap-stop: always;
}

Scroll Snap

Advanced

Single Snap

.snap-x {
  scroll-snap-type: x mandatory;

  & > :nth-child(5) {
    scroll-snap-align: center;
  }
}

Indirect Snap

.snap-x {
  scroll-snap-type: x mandatory;

  & > .some > .nested > .child {
    scroll-snap-align: center;
  }
}
🤘💀
🤘💀
🤘💀
🤘💀
🤘💀
🤘💀
🤘💀
🤘💀
🤘💀
🤘💀

Trapping Scroll

.hungry-overscroll-monster {
  overscroll-behavior: contain;
  overscroll-behavior: auto contain;
  overscroll-behavior-x: contain;
  overscroll-behavior-inline: contain;
}
Contained
Default

Scroll Padding

.scrollport-padding {
  --padding: 1rem;

  padding: var(--padding);
  scroll-padding: var(--padding);
}
No padding
Padding

Scroll Margin

.scroll-item-margin {
  scroll-margin: 1rem;
}
No margins
Margins

Scroll Into View

Element.scrollIntoView({
  behavior: 'smooth',
  inline:   'center',
})

Snap After Layout

Intersection Observer

let ioCallback = nodes => {
  // filter based on intersection/bounds/etc
}

let io = new IntersectionObserver(ioCallback, {
  root: scrollport,
})

for (let item of scrollitems)
  io.observe(item)

In View‽

let ioCallback = nodes => {
  for (let node of nodes)
    node.target.classList
      .toggle('in-view',
        node.isIntersecting)
}

Snap Gallery

https://codepen.io/collection/KpqBGW

A Codepen Collection

The Scrollport

  • Overflow axis
  • Scroll-snap-type
  • Gap, padding & scroll-padding
  • Overscroll-behavior

A Scroll Item

  • Snap-align
  • Snap-stop
  • Margin & scroll-margin

Scroll Snap

Tricks n' Hacks

Slides Starter Kit

.scrollport {
  scroll-snap-type: y mandatory;
}

.scroll-item {
  min-block-size: 100vh;
  scroll-snap-align: start;
  scroll-snap-stop: always;
}
slide 0
slide 1
slide 2
slide 3
slide 4
slide 5
slide 6
slide 7
slide 8
slide 9

Sticky Starter

.scroll-item {
  position: sticky;
  top: 0;
}
slide 0
slide 1
slide 2
slide 3
slide 4
slide 5
slide 6
slide 7
slide 8
slide 9

3D Perspective

.scrollport {
  perspective: 10px;
  perspective-origin: center center;
}

.scrollport, .scroll-item {
  transform-style: preserve-3d;
}

.scroll-item > p {
  transform: translateZ(-5px);
}

depth 0

depth 1

depth 2

depth 1

depth 2

depth 3

rad 🤘💀

Slyd Starter

<style>
  @import "https://unpkg.com/slyd/dist/slyd.css";
</style>

<script>
  import "https://unpkg.com/slyd";
</script>
Group A, Slide 1 Group A, Slide 2 Group A, Slide 3 Group B, Slide 1 Group B, Slide 2 Group B, Slide 3

Tap to Snap

.scrollport {
  overflow-x: auto;
  scroll-snap-type: x mandatory;
}

.scroll-item:is(:active, :focus) {
  scroll-snap-align: center;
}

Auto Forward

By Christian Schaefer

@keyframes slide {
  75%, 99% { top: 0 }
  95%, 98% { top: 100% }
}

@keyframes snap-toggle {
  96%, 100% { scroll-snap-align: start }
  97%, 99%  { scroll-snap-align: none }
}
slide 0
slide 1
slide 2
Chrome Only

Infinite Scroll

IntersectionObserver(nodes => {
  const [active] = nodes.filter(node =>
    node.isIntersecting)
  ...
  if (active.target === parent.lastElementChild)
    parent.firstElementChild.scrollIntoView()
}, {
  root: scrollPort
})
this
carousel
never
ends
this

S.M.A.R.T.

// on scrollend
// increase gap to 100vw
// el in view is snapped
// remove huge gap

Uncover which is snapped

Overscroll Effect

.scrollport > :not(.overscroller) {
  scroll-snap-align: start;
}

Scroll Start

@keyframes scroll-start {
  from {
    scroll-snap-align: center;
  }
  to {
    scroll-snap-align: unset;
  }
}

.starting-scroll-snap-target {
  animation: scroll-start 2ms;
}

Pull To Refresh

// tiny snap point at top of refresh el
// delayed snap point on tiny el
// scrollend checks scrollTop to be 0
// set loading attribute
// wait for fetch()
// scrollIntoView() main
// ScrollTimeline for flare
// snap-stop always on main for UX
Pull to refresh

Header

Lorem ipsum dolor sit amet consectetur adipisicing elit. Tempora laborum illo autem asperiores. Numquam voluptate facilis odit impedit non autem magni architecto, placeat voluptatum dolorem nemo doloremque velit, iure id.

Lorem ipsum dolor sit amet consectetur adipisicing elit. Tempora laborum illo autem asperiores. Numquam voluptate facilis odit impedit non autem magni architecto, placeat voluptatum dolorem nemo doloremque velit, iure id.

Lorem ipsum dolor sit amet consectetur adipisicing elit. Tempora laborum illo autem asperiores. Numquam voluptate facilis odit impedit non autem magni architecto, placeat voluptatum dolorem nemo doloremque velit, iure id.

Scroll Linked

new ScrollTimeline({
  scrollSource: tabsection,
  orientation: 'inline',
  fill: 'both',
})

⚠️ syntax changing

Sit dolor

Lorem ipsum dolor sit amet consectet adipisicing elit

Lore s sdf

Lorem ipsum dolor sit amet consectet adipisicing elit

Sit molestiae itaque rem optio molestias voluptati obcaecati!

Ipsum, a? Tenetur aut a nisi, aspernatur earum eligendi id quam nihil sint quas?

nisi, aspernatur earum eligendi id quam nihil sint quas?

Sit molestiae itaque rem optio molestias voluptati obcaecati!

Ipsum, a? Tenetur aut a nisi, aspernatur earum eligendi id quam nihil sint quas?

nisi, aspernatur earum eligendi id quam nihil sint quas?

Tenetur

Lorem ipsum dolor sit amet consectet adipisicing elit. Sit molestiae itaque rem optio molestias voluptati obcaecati!

Tenet urs

Ipsum, a? Tenetur aut a nisi, aspernatur earum eligendi id quam nihil sint quas?

nisi, aspernatur earum eligendi id quam nihil sint quas?

Lore s sdf

Lorem ipsum dolor sit amet consectet adipisicing elit

Sit molestiae itaque rem optio molestias voluptati obcaecati!

Ipsum, a? Tenetur aut a nisi, aspernatur earum eligendi id quam nihil sint quas?

nisi, aspernatur earum eligendi id quam nihil sint quas?

Lorems dolor

Lorem ipsum dolor sit amet consectet adipisicing elit

Sit molestiae itaque rem optio molestias voluptati obcaecati!

nisi, aspernatur earum eligendi id quam nihil sint quas?

Lore s sdf

Lorem ipsum dolor sit amet consectet adipisicing elit

Date / Time

.picker {
  display: grid;
  overflow-y: auto;
  overscroll-behavior-y: contain;
  scroll-snap-type: y mandatory;
}
Fri 24 Jul Sat 25 Jul Sun 26 Jul Mon 27 Jul Tue 28 Jul Wed 29 Jul Thu 30 Jul Fri 1 Aug Sat 2 Aug Sun 3 Aug Mon 4 Aug Tue 5 Aug Wed 6 Aug Thu 7 Aug Fri 8 Aug Sat 9 Aug Sun 10 Aug Mon 11 Aug Tue 12 Aug Wed 13 Aug Thu 14 Aug

Chat

.chat-bubble:last-child {
  scroll-snap-align: end;
}
Chat UI

Snap Bot

Hi!
I'm a bot
Hi!
I'm not a bot
But I am static text that was put here by a human. The only dynamic stuff you'll find here is whatever someone types in the "say something" text area hehe.

Snap Bot

Makes sense.
I see this chat implementation is using scroll snap points?
Can you tell me more about that?
sure!
scroll-snapping got an update and this is a demo to show the feature.
On Chrome 81, all these chats automatically snap to the scroll bottom. In Chrome 80, we'd need to write custom javascript to maintain the position at the bottom.
Now CSS can do it!

Stories

.scrollport {
  overflow-x: auto;
  scroll-snap-type: x mandatory;
}

.friend {
  scroll-snap-align: start;
  scroll-snap-stop: always;
}

Swipe UI

.swipe-ui {
  overflow-x: scroll;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
}

.swipe-message {
  scroll-snap-align: center;
}
archive
New Message!
delete
archive
New Message!
delete

Snap "next"

5 Features

Snap Explainers

https://github.com/argyleink/ScrollSnapExplainers/

Scroll Snap 2

Draft Module

https://drafts.csswg.org/css-scroll-snap-2/

:snapped

:snapped {
  outline: 1px solid hotpink;
}

:snapped-x {
  outline: 1px solid hotpink;
}
:snapped
Scroll Snap 2

Scroll Start

.salmon-block {
  scroll-start-target-inline: auto;
}

.scrollport {
  scroll-start: 0 5rem;
  scroll-start-y: 5rem;
}
scroll-start-target
Scroll Snap 2

snapChanging()

el.onsnapchanging = event => {
  console.info(event)
})
Scroll Snap 2

snapChanged()

el.onsnapchanged = event => {
  console.info(event)
})
Scroll Snap 2

snapTo()

scrollport.scrollTo({
  left: 'snap-prev',
  behavior: 'smooth',
})

Holy Snap

🤯 scroll 🤯

Review The Slides

oh-snap.netlify.app

Thank You