Scroll Snapping

Scrolling that settles at defined points.

1
2
3
4

Drag to scroll · mandatory snapping

html

<div class="mot-snp-wrap">
  <div class="mot-snp-track" data-strictness="mandatory" style="gap:12px;">
    <div class="mot-snp-card mot-snp-card--guide" style="background:#D9C8F0;">1</div>
    <div class="mot-snp-card mot-snp-card--guide" style="background:#D9C8F0;">2</div>
    <div class="mot-snp-card mot-snp-card--guide" style="background:#D9C8F0;">3</div>
    <div class="mot-snp-card mot-snp-card--guide" style="background:#D9C8F0;">4</div>
  </div>
  <p class="mot-snp-caption">Drag to scroll &middot; mandatory snapping</p>
</div>
<style>
.mot-snp-wrap { width: 100%; max-width: 320px; font: 12px system-ui, sans-serif; color: #241430aa; }
.mot-snp-track { display: flex; overflow-x: auto; padding: 14px; background: #FAF7F2; border: 1px solid #24143022; border-radius: 12px; scroll-snap-type: x mandatory; }
[data-strictness="proximity"] { scroll-snap-type: x proximity; }
.mot-snp-card { flex: 0 0 100px; height: 80px; border-radius: 10px; display: grid; place-items: center; font: 700 20px system-ui, sans-serif; color: #241430; scroll-snap-align: center; }
.mot-snp-card--guide { outline: 2px dashed #241430aa; outline-offset: 3px; }
.mot-snp-caption { margin: 8px 0 0; }
</style>

tailwind

<div class="w-full max-w-[320px] font-sans text-xs text-[#241430]/70">
  <div class="tw-snp-track flex overflow-x-auto p-3.5 bg-[#FAF7F2] border border-[#24143022] rounded-xl" data-strictness="mandatory" style="gap:12px; scroll-snap-type: x mandatory;">
    <div class="tw-snp-card tw-snp-guide flex-none w-[100px] h-20 rounded-[10px] grid place-items-center font-bold text-xl text-[#241430]" style="background:#D9C8F0; scroll-snap-align:center;">1</div>
    <div class="tw-snp-card tw-snp-guide flex-none w-[100px] h-20 rounded-[10px] grid place-items-center font-bold text-xl text-[#241430]" style="background:#D9C8F0; scroll-snap-align:center;">2</div>
    <div class="tw-snp-card tw-snp-guide flex-none w-[100px] h-20 rounded-[10px] grid place-items-center font-bold text-xl text-[#241430]" style="background:#D9C8F0; scroll-snap-align:center;">3</div>
    <div class="tw-snp-card tw-snp-guide flex-none w-[100px] h-20 rounded-[10px] grid place-items-center font-bold text-xl text-[#241430]" style="background:#D9C8F0; scroll-snap-align:center;">4</div>
  </div>
  <p class="mt-2">Drag to scroll &middot; mandatory snapping</p>
</div>
<style>
[data-strictness="proximity"] { scroll-snap-type: x proximity; }
.tw-snp-guide { outline: 2px dashed #241430aa; outline-offset: 3px; }
</style>

react

export function ScrollSnapping({
  strictness = "mandatory",
  gap = 12,
  showGuides = true,
  accent = "#D9C8F0",
}) {
  return (
    <div style={{ width: "100%", maxWidth: 320, fontFamily: "system-ui, sans-serif", fontSize: 12, color: "#241430aa" }}>
      <div style={{ display: "flex", overflowX: "auto", padding: 14, background: "#FAF7F2", border: "1px solid #24143022", borderRadius: 12, gap, scrollSnapType: `x ${strictness}` }}>
        {[1, 2, 3, 4].map((n) => (
          <div key={n} style={{ flex: "0 0 100px", height: 80, borderRadius: 10, display: "grid", placeItems: "center", fontWeight: 700, fontSize: 20, color: "#241430", background: accent, scrollSnapAlign: "center", outline: showGuides ? "2px dashed #241430aa" : "none", outlineOffset: 3 }}>
            {n}
          </div>
        ))}
      </div>
      <p style={{ margin: "8px 0 0" }}>Drag to scroll &middot; {strictness} snapping</p>
    </div>
  );
}