<!-- 
From: https://web.dev/building-a-stories-component/
Author: https://twitter.com/argyleink 
-->
<div class="stories">
  <section class="user">
    <article class="story" style="--bg: url(https://picsum.photos/480/841?random=1);"></article>
    <article class="story" style="--bg: url(https://picsum.photos/480/841?random=2);"></article>
    <article class="story" style="--bg: url(https://picsum.photos/480/841?random=3);"></article>
  </section>
  <section class="user">
    <article class="story" style="--bg: url(https://picsum.photos/480/841?random=12);"></article>
    <article class="story" style="--bg: url(https://picsum.photos/480/841?random=22);"></article>
    <article class="story" style="--bg: url(https://picsum.photos/480/841?random=32);"></article>
    <article class="story" style="--bg: url(https://picsum.photos/480/841?random=42);"></article>
  </section>
  <section class="user">
    <article class="story" style="--bg: url(https://picsum.photos/480/841?random=6412);"></article>
  </section>
  <section class="user">
    <article class="story" style="--bg: url(https://picsum.photos/480/841?random=412);"></article>
    <article class="story" style="--bg: url(https://picsum.photos/480/841?random=422);"></article>
    <article class="story" style="--bg: url(https://picsum.photos/480/841?random=432);"></article>
    <article class="story" style="--bg: url(https://picsum.photos/480/841?random=442);"></article>
  </section>
</div>
@import url("https://fonts.googleapis.com/css2?family=Exo:wght@600&display=swap");

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  width: 100vw;
  min-height: 100vh;
  font-family: "Exo", Arial, sans-serif;
  display: grid;
  align-items: center;
  justify-items: center;
  place-items: center;
  background: hsl(200, 15%, 93%);
}

.stories {
  width: 100vw;
  height: 100vh;

  box-shadow: 0 5px 2.5px hsla(200, 95%, 3%, 0.037),
    0 12px 6.5px hsla(200, 95%, 3%, 0.053),
    0 22.5px 13px hsla(200, 95%, 3%, 0.065),
    0 40.2px 24px hsla(200, 95%, 3%, 0.077),
    0 75.2px 44px hsla(200, 95%, 3%, 0.093),
    0 180px 80px hsla(200, 95%, 3%, 0.13);

  display: grid;
  grid: 1fr / auto-flow 100%;
  grid-gap: 1ch;
  gap: 1ch;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  overscroll-behavior: contain;
  touch-action: pan-x;
}

.user {
  /* outer */
  scroll-snap-align: start;
  scroll-snap-stop: always;

  /* inner */
  display: grid;
  grid: [story] 1fr / [story] 1fr;
}

.story {
  grid-area: story;

  background-size: cover;
  background-image: var(--bg),
    linear-gradient(to top, rgb(249, 249, 249), rgb(226, 226, 226));

  user-select: none;
  touch-action: manipulation;

  transition: opacity 0.3s cubic-bezier(0.4, 0, 1, 1);
}

.story.seen {
  opacity: 0;
  pointer-events: none;
}

@media (hover: hover) {
  .stories {
    border-radius: 3ch;
  }
}

@media (hover: hover) and (min-width: 480px) {
  .stories {
    max-width: 480px;
    max-height: 848px;
  }
}

@media (hover: hover) and (max-height: 880px) and (min-width: 720px) {
  .stories {
    max-width: 320px;
    max-height: 568px;
  }
}
const stories = document.querySelector(".stories");
const median = stories.offsetLeft + stories.clientWidth / 2;
const state = {
  current_story: stories.firstElementChild.lastElementChild
};

const navigateStories = (direction) => {
  const story = state.current_story;
  const lastItemInUserStory = story.parentNode.firstElementChild;
  const firstItemInUserStory = story.parentNode.lastElementChild;
  const hasNextUserStory = story.parentElement.nextElementSibling;
  const hasPrevUserStory = story.parentElement.previousElementSibling;

  if (direction === "next") {
    if (lastItemInUserStory === story && !hasNextUserStory) return;
    else if (lastItemInUserStory === story && hasNextUserStory) {
      state.current_story =
        story.parentElement.nextElementSibling.lastElementChild;
      story.parentElement.nextElementSibling.scrollIntoView({
        behavior: "smooth"
      });
    } else {
      story.classList.add("seen");
      state.current_story = story.previousElementSibling;
    }
  } else if (direction === "prev") {
    if (firstItemInUserStory === story && !hasPrevUserStory) return;
    else if (firstItemInUserStory === story && hasPrevUserStory) {
      state.current_story =
        story.parentElement.previousElementSibling.firstElementChild;
      story.parentElement.previousElementSibling.scrollIntoView({
        behavior: "smooth"
      });
    } else {
      story.nextElementSibling.classList.remove("seen");
      state.current_story = story.nextElementSibling;
    }
  }
};

stories.addEventListener("click", (e) => {
  if (e.target.nodeName !== "ARTICLE") return;
  navigateStories(e.clientX > median ? "next" : "prev");
}); // left & right are free with snap points 👍

document.addEventListener("keydown", ({ key }) => {
  if (key !== "ArrowDown" || key !== "ArrowUp")
    navigateStories(key === "ArrowDown" ? "next" : "prev");
});

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.