Components Medium
Collection Carousel
Auto-scrolling horizontal carousel with snap points, arrow navigation, pause on hover, and a collection selector.
Open in Lab
MCP
css javascript
Targets: HTML
Code
:root {
color-scheme: dark;
font-family: "Inter", "Segoe UI", system-ui, -apple-system, sans-serif;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
min-height: 100vh;
background: #080808;
color: #e2e8f0;
display: grid;
place-items: center;
}
.stage {
width: 100%;
max-width: 1280px;
padding: 3rem 2.5rem; /* room for the -18px arrows on both sides */
}
/* โโโ Carousel container โโโ */
.carousel-wrap {
position: relative;
}
/* โโโ Arrow buttons โโโ */
.arrow {
position: absolute;
top: 50%;
z-index: 10;
transform: translateY(-50%);
width: 36px;
height: 36px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.06);
border: 1px solid rgba(255, 255, 255, 0.1);
color: rgba(255, 255, 255, 0.5);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: background 0.15s, color 0.15s;
}
.arrow:hover {
background: rgba(255, 255, 255, 0.12);
color: #f8fafc;
}
.arrow-prev { left: -18px; }
.arrow-next { right: -18px; }
/* โโโ Scrollable track โโโ */
.carousel-track {
display: flex;
gap: 14px;
overflow-x: auto;
scroll-snap-type: x mandatory;
scrollbar-width: none;
padding: 8px 2px;
list-style: none;
}
.carousel-track::-webkit-scrollbar {
display: none;
}
/* โโโ Cards โโโ */
.carousel-card {
flex: 0 0 220px;
scroll-snap-align: start;
padding: 1.5rem 1.25rem;
border-radius: 14px;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.08);
display: flex;
flex-direction: column;
gap: 0.625rem;
transition: border-color 0.2s, background 0.2s;
cursor: default;
}
.carousel-card:hover {
border-color: rgba(255, 255, 255, 0.14);
background: rgba(255, 255, 255, 0.05);
}
.card-label {
font-size: 0.875rem;
font-weight: 500;
color: rgba(255, 255, 255, 0.65);
}
.card-count {
font-size: 1.75rem;
font-weight: 700;
color: #f8fafc;
line-height: 1;
}
/* โโโ Responsive โโโ */
@media (max-width: 640px) {
.stage { padding: 2rem 1rem; }
/* Hide arrows on mobile โ use touch swipe instead */
.arrow { display: none; }
/* Show partial next card as scroll hint */
.carousel-track { padding: 8px 0; }
.carousel-card { flex: 0 0 min(200px, 72vw); }
}
@media (max-width: 360px) {
.carousel-card { flex: 0 0 82vw; }
}<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Collection Carousel</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<main class="stage">
<div class="carousel-wrap" id="carousel-wrap">
<!-- Prev / Next arrows -->
<button class="arrow arrow-prev" id="prev" aria-label="Previous">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
<polyline points="15 18 9 12 15 6"/>
</svg>
</button>
<button class="arrow arrow-next" id="next" aria-label="Next">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
<polyline points="9 18 15 12 9 6"/>
</svg>
</button>
<!-- Track -->
<ul class="carousel-track" id="carousel-track" role="list">
<li class="carousel-card"><span class="card-label">UI Components</span><span class="card-count">311</span></li>
<li class="carousel-card"><span class="card-label">Web Animations</span><span class="card-count">44</span></li>
<li class="carousel-card"><span class="card-label">Patterns</span><span class="card-count">55</span></li>
<li class="carousel-card"><span class="card-label">Pages</span><span class="card-count">59</span></li>
<li class="carousel-card"><span class="card-label">Design Styles</span><span class="card-count">26</span></li>
<li class="carousel-card"><span class="card-label">Architectures</span><span class="card-count">13</span></li>
<li class="carousel-card"><span class="card-label">Boilerplates</span><span class="card-count">11</span></li>
<li class="carousel-card"><span class="card-label">Components</span><span class="card-count">11</span></li>
</ul>
</div>
</main>
<script>
const track = document.getElementById('carousel-track');
const wrap = document.getElementById('carousel-wrap');
const INTERVAL = 3000;
let timer;
let paused = false;
function cardWidth() {
const card = track.querySelector('.carousel-card');
return card ? card.offsetWidth + parseInt(getComputedStyle(track).gap) : 240;
}
function scrollBy(dir) {
track.scrollBy({ left: dir * cardWidth(), behavior: 'smooth' });
}
function autoStep() {
const { scrollLeft, scrollWidth, clientWidth } = track;
if (scrollLeft + clientWidth >= scrollWidth - 4) {
track.scrollTo({ left: 0, behavior: 'smooth' });
} else {
scrollBy(1);
}
}
function startAuto() { timer = setInterval(autoStep, INTERVAL); }
function stopAuto() { clearInterval(timer); }
document.getElementById('prev').addEventListener('click', () => { stopAuto(); scrollBy(-1); if (!paused) startAuto(); });
document.getElementById('next').addEventListener('click', () => { stopAuto(); scrollBy( 1); if (!paused) startAuto(); });
wrap.addEventListener('mouseenter', () => { paused = true; stopAuto(); });
wrap.addEventListener('mouseleave', () => { paused = false; startAuto(); });
wrap.addEventListener('focusin', () => { paused = true; stopAuto(); });
wrap.addEventListener('focusout', () => { paused = false; startAuto(); });
startAuto();
</script>
</body>
</html>Collection Carousel
A horizontal scrollable carousel that auto-advances every few seconds. Cards snap to position, left/right arrow buttons allow manual navigation, and the auto-scroll pauses when the user hovers or focuses.
Features
- CSS scroll snap for clean card alignment
- Auto-scroll with configurable interval
- Pause on hover / focus
- Previous / Next buttons with boundary awareness
- Keyboard navigable
When to use
- Featured content or collection showcase sections
- Image or card galleries
- Landing page content sections