Basic Carousel
A slide carousel with prev/next arrows, dots and a polite live region.
6 个框架中级
A transform carousel turned on its side, paging up and down.
<!-- Same transform carousel, turned 90deg: the track is a flex column and the
move is translateY, not translateX. Up/Down replace Prev/Next, and the
stage needs an explicit height because the slides no longer fill it. -->
<section class="max-w-md" aria-roledescription="carousel" aria-label="Product highlights" data-vcar>
<div class="flex gap-3">
<div class="relative h-56 flex-1 overflow-hidden rounded-xl" aria-live="polite">
<div class="flex h-full flex-col transition-transform duration-500 ease-out motion-reduce:transition-none" data-vcar-track>
<div class="flex h-full shrink-0 basis-full flex-col justify-end bg-gradient-to-br from-blue-600 to-indigo-600 px-6 py-6 text-white" role="group" aria-roledescription="slide" aria-label="1 of 3">
<h3 class="mb-1 text-lg font-semibold">Analytics</h3><p class="text-sm leading-relaxed text-white/90">Every metric that matters, updated the moment it changes.</p>
</div>
<div class="flex h-full shrink-0 basis-full flex-col justify-end bg-gradient-to-br from-indigo-600 to-violet-600 px-6 py-6 text-white" role="group" aria-roledescription="slide" aria-label="2 of 3" aria-hidden="true">
<h3 class="mb-1 text-lg font-semibold">Automations</h3><p class="text-sm leading-relaxed text-white/90">Turn a repeated click into a rule that runs itself.</p>
</div>
<div class="flex h-full shrink-0 basis-full flex-col justify-end bg-gradient-to-br from-teal-700 to-sky-700 px-6 py-6 text-white" role="group" aria-roledescription="slide" aria-label="3 of 3" aria-hidden="true">
<h3 class="mb-1 text-lg font-semibold">Integrations</h3><p class="text-sm leading-relaxed text-white/90">Forty connectors, and a webhook for everything else.</p>
</div>
</div>
</div>
<div class="flex flex-col items-center justify-center gap-3">
<button class="grid h-9 w-9 place-items-center rounded-full border border-gray-200 bg-white text-gray-700 hover:bg-gray-50 hover:text-gray-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 focus-visible:ring-offset-2 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-gray-700 dark:hover:text-white dark:focus-visible:ring-blue-400 dark:focus-visible:ring-offset-gray-950" type="button" aria-label="Previous slide" data-vcar-prev>
<svg class="h-[1.125rem] w-[1.125rem]" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="m5 12.5 5-5 5 5" stroke-linecap="round" stroke-linejoin="round" /></svg>
</button>
<div class="flex flex-col gap-2" data-vcar-dots>
<button class="h-2 w-2 rounded-full bg-gray-500 hover:bg-gray-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 focus-visible:ring-offset-2 aria-[current=true]:h-5 aria-[current=true]:bg-blue-600 dark:hover:bg-gray-400 dark:focus-visible:ring-blue-400 dark:focus-visible:ring-offset-gray-950 dark:aria-[current=true]:bg-blue-400" type="button" aria-label="Go to slide 1" aria-current="true"></button>
<button class="h-2 w-2 rounded-full bg-gray-500 hover:bg-gray-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 focus-visible:ring-offset-2 aria-[current=true]:h-5 aria-[current=true]:bg-blue-600 dark:hover:bg-gray-400 dark:focus-visible:ring-blue-400 dark:focus-visible:ring-offset-gray-950 dark:aria-[current=true]:bg-blue-400" type="button" aria-label="Go to slide 2"></button>
<button class="h-2 w-2 rounded-full bg-gray-500 hover:bg-gray-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 focus-visible:ring-offset-2 aria-[current=true]:h-5 aria-[current=true]:bg-blue-600 dark:hover:bg-gray-400 dark:focus-visible:ring-blue-400 dark:focus-visible:ring-offset-gray-950 dark:aria-[current=true]:bg-blue-400" type="button" aria-label="Go to slide 3"></button>
</div>
<button class="grid h-9 w-9 place-items-center rounded-full border border-gray-200 bg-white text-gray-700 hover:bg-gray-50 hover:text-gray-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 focus-visible:ring-offset-2 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-gray-700 dark:hover:text-white dark:focus-visible:ring-blue-400 dark:focus-visible:ring-offset-gray-950" type="button" aria-label="Next slide" data-vcar-next>
<svg class="h-[1.125rem] w-[1.125rem]" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="m5 7.5 5 5 5-5" stroke-linecap="round" stroke-linejoin="round" /></svg>
</button>
</div>
</div>
</section>
<script>
(function () {
document.querySelectorAll('[data-vcar]').forEach(function (root) {
var track = root.querySelector('[data-vcar-track]');
var slides = Array.prototype.slice.call(track.children);
var dots = Array.prototype.slice.call(root.querySelectorAll('[data-vcar-dots] button'));
var index = 0;
function go(next) {
index = (next + slides.length) % slides.length;
track.style.transform = 'translateY(' + index * -100 + '%)';
slides.forEach(function (s, i) { if (i === index) s.removeAttribute('aria-hidden'); else s.setAttribute('aria-hidden', 'true'); });
dots.forEach(function (d, i) { if (i === index) d.setAttribute('aria-current', 'true'); else d.removeAttribute('aria-current'); });
}
root.querySelector('[data-vcar-prev]').addEventListener('click', function () { go(index - 1); });
root.querySelector('[data-vcar-next]').addEventListener('click', function () { go(index + 1); });
dots.forEach(function (d, i) { d.addEventListener('click', function () { go(i); }); });
});
})();
</script>| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
items必填 | Slide[] | - | 要渲染的项目数组。 |
className | string | - | 合并到根元素上的额外类名。 |
ariaLabel | string | 'Carousel' | 按钮的无障碍名称。仅有图标的按钮必须设置。 |
The track is a flex column translated with `translateY`, and the stage carries an explicit height because the slides no longer size it. Up/Down buttons and a vertical dot rail replace the usual horizontal controls; reduced motion turns the glide into a cut.