About this snippet

Five slides sit in a row. Swipe or scroll to move between them, click the arrows at either side, or click a dot underneath, and every slide snaps into place.

The track is a plain scroll container with scroll-snap-type on the inline axis and scroll-snap-align on each slide, which is what makes swiping land cleanly. The arrows are the ::scroll-button pseudo-elements, real buttons the browser renders that scroll the container by most of its visible width and disable themselves at either end. The dots come from scroll-marker-group on the container and ::scroll-marker on each slide, with :target-current styling the one for the slide in view.

Scroll snap works everywhere. ::scroll-button and ::scroll-marker landed in Chrome and Edge in 2025, so in other browsers the carousel is a swipeable snap track without the arrows and dots.

Size
3.9 KB
Uses
HTML, CSS
Loads
Nothing external
Views
63
<section class="carousel-wrap">
  <h1>Featured articles</h1>
  <div class="carousel">
    <article class="slide slide-1">
      <span class="tag">Design</span>
      <h2>Designing interfaces that age well</h2>
      <p>Why restraint, real content and consistent spacing outlast every trend.</p>
    </article>
    <article class="slide slide-2">
      <span class="tag">CSS</span>
      <h2>Container queries in production</h2>
      <p>Patterns from a year of shipping components that size themselves.</p>
    </article>
    <article class="slide slide-3">
      <span class="tag">Performance</span>
      <h2>Cutting a second off first paint</h2>
      <p>Fonts, critical CSS and the two image mistakes almost every site makes.</p>
    </article>
    <article class="slide slide-4">
      <span class="tag">Accessibility</span>
      <h2>Focus states people can actually see</h2>
      <p>A checklist for rings, contrast and skip links that hold up in testing.</p>
    </article>
    <article class="slide slide-5">
      <span class="tag">JavaScript</span>
      <h2>Less script, same interface</h2>
      <p>Native dialogs, popovers and scroll snap replacing three libraries.</p>
    </article>
  </div>
</section>
*, *::before, *::after {
  box-sizing: border-box;
}

body {
  font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  margin: 0;
  background: #0f172a;
  color: #f8fafc;
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
  padding: 20px;
}

.carousel-wrap {
  width: 100%;
  max-width: 720px;
}

.carousel-wrap h1 {
  font-size: 1.25rem;
  margin: 0 0 16px;
  color: #94a3b8;
  font-weight: 600;
}

.carousel {
  display: flex;
  gap: 16px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scrollbar-width: none;
  scroll-marker-group: after;
  anchor-name: --carousel;
}

.carousel::-webkit-scrollbar {
  display: none;
}

.slide {
  flex: 0 0 100%;
  scroll-snap-align: start;
  min-height: 300px;
  padding: 36px;
  border-radius: 20px;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}

.slide-1 { background: linear-gradient(135deg, #1d4ed8, #0ea5e9); }
.slide-2 { background: linear-gradient(135deg, #7c3aed, #db2777); }
.slide-3 { background: linear-gradient(135deg, #059669, #84cc16); }
.slide-4 { background: linear-gradient(135deg, #ea580c, #facc15); }
.slide-5 { background: linear-gradient(135deg, #334155, #64748b); }

.tag {
  align-self: flex-start;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  background: rgba(255, 255, 255, 0.2);
  padding: 5px 10px;
  border-radius: 999px;
  margin-bottom: 14px;
}

.slide h2 {
  font-size: 1.625rem;
  line-height: 1.15;
  margin: 0 0 8px;
  letter-spacing: -0.02em;
}

.slide p {
  margin: 0;
  color: rgba(255, 255, 255, 0.85);
  line-height: 1.5;
}

.carousel::scroll-button(*) {
  position: absolute;
  position-anchor: --carousel;
  width: 44px;
  height: 44px;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.92);
  color: #0f172a;
  font-size: 1.25rem;
  cursor: pointer;
  transition: background 0.15s ease, opacity 0.15s ease;
}

.carousel::scroll-button(*):hover {
  background: #ffffff;
}

.carousel::scroll-button(*):disabled {
  opacity: 0.3;
  cursor: default;
}

.carousel::scroll-button(left) {
  content: "\2190" / "Previous";
  position-area: center inline-start;
  margin-right: -24px;
}

.carousel::scroll-button(right) {
  content: "\2192" / "Next";
  position-area: center inline-end;
  margin-left: -24px;
}

.carousel::scroll-marker-group {
  display: flex;
  justify-content: center;
  gap: 8px;
  padding: 18px 0 0;
}

.slide::scroll-marker {
  content: "";
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #334155;
  transition: background 0.15s ease, transform 0.15s ease;
}

.slide::scroll-marker:hover {
  transform: scale(1.3);
}

.slide::scroll-marker:target-current {
  background: #f8fafc;
}
Esc
↑↓ moveEnter select

Start from a template

Open an .html file, or HTML, CSS, JS, Markdown, Less or TypeScript files. You can also drop them here. Styles and scripts go into their panes.

Share

Source

Keyboard shortcuts

Save