Lesson 23 / Components

Carousels

Time
51 min
Type
Reading + Interactive
Level
Intermediate
Use
Stretch

Design carousels carefully, prioritize manual controls, and build accessible slide navigation when a carousel is truly useful.

Course Role

Stretch

Good for students who are ready to go further after the core version works.

Teacher Notes / In-Class Use

Demo Live

  • Use the carousel lab to show that controls, pagination, status text, keyboard support, and motion settings are separate concerns.
  • Show the Splide configuration first, then explain what the hidden teaching JavaScript adds for the interactive lab.

Try In Class

  • Have students identify whether their carousel needs previous and next buttons, dots, autoplay, or none of the above.
  • Ask students to test the carousel with keyboard only before styling it.

Submit Or Check

  • There should be a visible way to move between slides without relying on autoplay.
  • If autoplay exists, students should include a pause strategy and avoid surprising motion.

Watch For

  • All slides being visible because the carousel CSS or initialization did not load.
  • Autoplay treated as decoration while keyboard and status feedback are ignored.

Learning Goals

  • Decide when a `carousel` is appropriate and when another pattern is better
  • Identify the parts of an accessible carousel, including slides, buttons, status text, and pagination
  • Build manual previous/next behavior before adding optional autoplay
  • Test carousel behavior with keyboard, reduced motion, screen reader expectations, and responsive layouts

Interactive Demo

How to use this demo.

Use the demo as a small lab. Change one thing, observe the result, then connect it back to your own project.

What To Try

  • Toggle controls, dots, visible status, keyboard support, autoplay, and pause behavior.
  • Use the previous and next buttons, dots, and arrow keys to change slides.

What Changes

  • The carousel preview, checklist, and Splide code sample update together.
  • Removing controls or status makes the interaction harder to understand.

What To Notice

  • A carousel is not only slides. It needs controls, state, keyboard support, and motion decisions.
  • Autoplay increases responsibility because users need ways to pause or control motion.

Apply It

  • Check your carousel for visible controls, one active slide, keyboard access, and a pause strategy if autoplay is enabled.

This demo uses extra JavaScript for teaching. The code sample shows the pattern to practice. View full demo source.

What Are Carousels?

A carousel displays one item or group of items at a time from a larger set. Users move through the items with controls such as previous and next buttons, pagination dots, or sometimes autoplay.

Carousels can be useful, but they can also hide content, distract users, and create accessibility problems. Use one only when it helps the user browse a small set of related content.

Should You Use a Carousel?

SituationRecommendationWhy
A small set of related images or testimonialsMaybe use a carouselUsers can browse a focused set manually.
Critical content users must seeAvoid a carouselHidden slides may never be seen.
Many cards or productsUse a grid or listing pageBrowsing and comparison are easier when more items are visible.
Content users need to read carefullyAvoid autoplayChanging slides can interrupt reading.
A homepage hero with one main messageUse a static heroOne strong message is clearer than rotating messages.

Better Alternatives

Before choosing a carousel, consider whether a simpler pattern would serve users better.

AlternativeUse When
Card gridUsers need to scan or compare several items.
Featured content sectionOne or two items deserve emphasis.
TabsA small set of related panels should be directly selectable.
AccordionContent should expand and collapse in place.
Static hero with linksThe first screen needs one clear message and next step.

Carousel Anatomy

PartPurpose
Region or containerGroups the carousel and gives it an accessible name.
SlidesHold the image, text, or content item being shown.
Previous and next button controlsLet users move manually through slides.
Slide position textCommunicates status, such as Slide 2 of 4.
Pagination buttonsLet users jump to a specific slide.
Pause/play controlRequired if autoplay is used.

Accessible Carousel Markup

Start with semantic markup and manual controls. Use real button elements for actions. Hide inactive slides with hidden so their links and buttons are not tabbable.

<section class="carousel" aria-label="Featured projects" aria-roledescription="carousel">
  <p class="carousel-status" aria-live="polite">Slide 1 of 3</p>

  <div class="carousel-slides">
    <article class="slide" aria-label="Slide 1 of 3">
      <img
        src="project-dashboard.webp"
        width="900"
        height="600"
        alt="Dashboard interface with charts and project cards"
      >
      <h2>Portfolio Dashboard</h2>
      <p>A responsive dashboard for tracking project progress.</p>
    </article>

    <article class="slide" aria-label="Slide 2 of 3" hidden>
      <h2>Course Landing Page</h2>
      <p>A landing page with pricing, testimonials, and enrollment CTA.</p>
    </article>

    <article class="slide" aria-label="Slide 3 of 3" hidden>
      <h2>Gallery Experience</h2>
      <p>An image-heavy project page with optimized media.</p>
    </article>
  </div>

  <button class="carousel-prev" type="button">Previous</button>
  <button class="carousel-next" type="button">Next</button>
</section>

Basic Carousel CSS

Keep dimensions stable so the page does not jump between slides. Make controls large enough for mouse, keyboard, and touch users.

.carousel {
  display: grid;
  gap: 1rem;
}

.slide img {
  aspect-ratio: 3 / 2;
  width: 100%;
  height: auto;
  object-fit: cover;
}

.carousel-prev,
.carousel-next {
  min-inline-size: 2.75rem;
  min-block-size: 2.75rem;
}

.carousel button:focus-visible {
  outline: 3px solid currentColor;
  outline-offset: 0.25rem;
}

Manual JavaScript First

Build manual previous and next behavior before adding autoplay. Users should always be able to control the carousel themselves.

const carousel = document.querySelector('.carousel');
const slides = [...carousel.querySelectorAll('.slide')];
const status = carousel.querySelector('.carousel-status');
const previousButton = carousel.querySelector('.carousel-prev');
const nextButton = carousel.querySelector('.carousel-next');
let currentIndex = 0;

function showSlide(index) {
  currentIndex = (index + slides.length) % slides.length;

  slides.forEach((slide, slideIndex) => {
    slide.hidden = slideIndex !== currentIndex;
  });

  status.textContent = 'Slide ' + (currentIndex + 1) + ' of ' + slides.length;
}

previousButton.addEventListener('click', () => {
  showSlide(currentIndex - 1);
});

nextButton.addEventListener('click', () => {
  showSlide(currentIndex + 1);
});

Pagination Buttons

Pagination buttons let users jump directly to a slide. Use aria-current="true" on the active pagination button.

<div class="carousel-pagination" aria-label="Choose slide">
  <button type="button" aria-current="true">1</button>
  <button type="button">2</button>
  <button type="button">3</button>
</div>

Optional Autoplay Rules

Autoplay should be optional, easy to pause, and respectful of user motion preferences. Do not autoplay content users need to read carefully.

  • Provide a pause/play button if slides advance automatically.
  • Pause autoplay when the carousel receives keyboard focus.
  • Pause autoplay on pointer hover.
  • Use clearInterval when pausing or destroying autoplay.
  • Do not autoplay when prefers-reduced-motion is set to reduce.

Autoplay with Guardrails

const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
let autoplayId;

function startAutoplay() {
  if (reduceMotion || autoplayId) return;

  autoplayId = setInterval(() => {
    showSlide(currentIndex + 1);
  }, 5000);
}

function stopAutoplay() {
  clearInterval(autoplayId);
  autoplayId = undefined;
}

carousel.addEventListener('mouseenter', stopAutoplay);
carousel.addEventListener('focusin', stopAutoplay);

Keyboard Behavior

  • Tab should reach the carousel controls and any interactive content in the visible slide.
  • Inactive slides should not contain tabbable links or buttons. Use hidden or manage focus carefully.
  • Arrow key shortcuts are optional. Do not require them for basic use.
  • Focus should not jump unexpectedly when the slide changes.
  • The carousel should remain understandable if JavaScript fails.

Responsive and Image Guidance

  • Use consistent image dimensions or aspect ratios to prevent layout shift.
  • Give informative images meaningful alt text and decorative images empty alt="".
  • Keep controls reachable on touch screens.
  • Do not lock the carousel to a height that causes text overflow on small screens.
  • Do not lazy-load the first visible slide image if it is above the fold.

Modern Library Option: Splide

Splide is a modern carousel library that works without jQuery. Use it when a project needs a polished carousel faster than you can build and test one from scratch.

The same rules still apply: keep controls visible, avoid autoplay by default, write useful slide content, and test keyboard, screen reader, motion, and responsive behavior.

A library can save time, but it does not make the carousel automatically accessible or appropriate for the page.

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@splidejs/splide@4/dist/css/splide.min.css">

<section class="splide" aria-label="Featured projects">
  <div class="splide__track">
    <ul class="splide__list">
      <li class="splide__slide">Project 1</li>
      <li class="splide__slide">Project 2</li>
      <li class="splide__slide">Project 3</li>
    </ul>
  </div>
</section>

<script src="https://cdn.jsdelivr.net/npm/@splidejs/splide@4/dist/js/splide.min.js"></script>
<script>
  new Splide('.splide', {
    arrows: true,
    pagination: true,
    autoplay: false,
  }).mount();
</script>

Common Mistakes

MistakeWhy It HurtsFix
Autoplay with no pauseUsers lose control of moving contentAdd pause/play and pause on focus or hover.
Using a carousel for important contentHidden slides may never be seenPut critical content directly on the page.
Tiny controlsControls are hard to use on touch screensUse large, visible button controls.
Controls only appear on hoverKeyboard and touch users may not find themKeep controls visible or reveal them on focus too.
Hidden slides are still tabbableKeyboard users can tab into invisible contentUse hidden or remove inactive content from the tab order.
Announcements are too aggressiveScreen reader users may be interruptedUse aria-live="polite" carefully and avoid constant autoplay announcements.
Images have inconsistent sizesThe page jumps between slidesUse stable dimensions and aspect ratios.

Checkpoint

Before moving on, make sure these feel true.

  • I can explain what controls, dots, status text, and autoplay each do.
  • I can verify only one carousel slide is visually active at a time.
  • I can test carousel navigation without relying on a mouse.

Project Connection

This lesson supports current class projects.

Practice

  • Build static carousel markup with three slides.
  • Add previous and next button controls.
  • Hide inactive slides with hidden.
  • Add slide count text such as Slide 1 of 3.
  • Add optional pagination buttons with aria-current="true".
  • Add pause/play only if you add autoplay.
  • Test the carousel with keyboard navigation and reduced motion settings.

Resources