Stap 9 — Carousel (Bootstrap 5, zonder autoplay)

We bouwen een toegankelijke carousel die niet automatisch draait. Gebruikers hebben volledige controle (pijltjes, toetsenbord, swipe) en zien een status (“Dia X van N”).

9.1 Markup

Plaats dit blok waar je de carousel wil tonen.

<section aria-label="Projecten">

  <div id="carouselProjecten"
       class="carousel slide"
       data-bs-ride="false"
       data-bs-interval="false"
       data-bs-touch="true"
       data-bs-keyboard="true"
       aria-roledescription="carousel"
       aria-label="Projecten carousel">

    <!-- Indicators (klikbare bullets) -->
    <div class="carousel-indicators">
      <button type="button" data-bs-target="#carouselProjecten" data-bs-slide-to="0" class="active"
              aria-current="true" aria-label="Dia 1"></button>
      <button type="button" data-bs-target="#carouselProjecten" data-bs-slide-to="1" aria-label="Dia 2"></button>
      <button type="button" data-bs-target="#carouselProjecten" data-bs-slide-to="2" aria-label="Dia 3"></button>
    </div>

    <div class="carousel-inner">

      <!-- Slide 1 -->
      <div class="carousel-item active">
        <picture>
          <source srcset="assets/img/proj1-1280.webp" type="image/webp">
          <img src="assets/img/proj1-1280.jpg" class="d-block w-100" width="1280" height="720"
               alt="Posterontwerp 4GT — kleurrijke typografie">
        </picture>
        <div class="carousel-caption d-none d-md-block caption-glass">
          <h3 class="h5 mb-1">Poster 4GT</h3>
          <p class="mb-0">Kleurstudie en typografie-experiment.</p>
        </div>
      </div>

      <!-- Slide 2 -->
      <div class="carousel-item">
        <picture>
          <source srcset="assets/img/proj2-1280.webp" type="image/webp">
          <img src="assets/img/proj2-1280.jpg" class="d-block w-100" width="1280" height="720"
               alt="Packaging mock-up — doosje met patroon" loading="lazy">
        </picture>
        <div class="carousel-caption d-none d-md-block caption-glass">
          <h3 class="h5 mb-1">Packaging</h3>
          <p class="mb-0">Kartonnen doosje met eigen patroon.</p>
        </div>
      </div>

      <!-- Slide 3 -->
      <div class="carousel-item">
        <picture>
          <source srcset="assets/img/proj3-1280.webp" type="image/webp">
          <img src="assets/img/proj3-1280.jpg" class="d-block w-100" width="1280" height="720"
               alt="Website lay-out — landing met hero en kaarten" loading="lazy">
        </picture>
        <div class="carousel-caption d-none d-md-block caption-glass">
          <h3 class="h5 mb-1">Web lay-out</h3>
          <p class="mb-0">Hero + kaarten, mobile-first.</p>
        </div>
      </div>

    </div>

    <!-- Controls -->
    <button class="carousel-control-prev" type="button" data-bs-target="#carouselProjecten" data-bs-slide="prev">
      <span class="carousel-control-prev-icon" aria-hidden="true"></span>
      <span class="visually-hidden">Vorige</span>
    </button>
    <button class="carousel-control-next" type="button" data-bs-target="#carouselProjecten" data-bs-slide="next">
      <span class="carousel-control-next-icon" aria-hidden="true"></span>
      <span class="visually-hidden">Volgende</span>
    </button>

  </div>

  <!-- Status + optionele Afspelen/Pauzeren -->
  <div class="d-flex align-items-center justify-content-between mt-2">
    <p id="carouselProjectenStatus" class="m-0 small visually-hidden" aria-live="polite">Dia 1 van 3</p>
    <button id="carouselProjectenToggle" type="button" class="btn btn-outline-primary btn-sm">Afspelen</button>
  </div>

</section>

9.2 CSS (thematiseren met tokens)

Voeg toe aan assets/css/custom.css (kleine tweaks, netjes subtiel):

/* Carousel kleurt mee met je tokens */
#carouselProjecten{
  --bs-carousel-control-color: #fff;
  --bs-carousel-control-opacity: .85;
  --bs-carousel-indicator-active-bg: var(--brand-primary);
  --bs-carousel-indicator-hit-area-height: 2rem;
}

/* Glasachtige caption voor leesbaarheid op foto */
.caption-glass{
  background: color-mix(in oklab, var(--brand-bg), black 25%);
  color: #fff;
  border-radius: .5rem;
  padding: .5rem .75rem;
  display: inline-block;
  backdrop-filter: saturate(140%) blur(6px);
}

9.3 JS (status + Afspelen/Pauzeren)

Klein script in assets/js/main.js om de status te tonen en optioneel te laten afspelen.

document.addEventListener('DOMContentLoaded', () => {
  const el = document.getElementById('carouselProjecten');
  if (!el || !window.bootstrap) return;

  const carousel = new bootstrap.Carousel(el, {
    interval: false, // geen autoplay
    ride: false,
    touch: true,
    keyboard: true,
    wrap: true
  });

  const items = el.querySelectorAll('.carousel-item');
  const status = document.getElementById('carouselProjectenStatus');
  const toggle = document.getElementById('carouselProjectenToggle');

  const updateStatus = (index) => {
    if (status) status.textContent = `Dia ${index + 1} van ${items.length}`;
  };

  // init
  let current = [...items].findIndex(i => i.classList.contains('active'));
  updateStatus(current >= 0 ? current : 0);

  el.addEventListener('slid.bs.carousel', (e) => {
    const i = [...items].indexOf(e.relatedTarget);
    updateStatus(i);
  });

  // Afspelen / Pauzeren (optioneel)
  let playing = false;
  if (toggle) {
    toggle.addEventListener('click', () => {
      playing = !playing;
      if (playing) {
        carousel._config.interval = 4000; // interval aanzetten
        carousel.cycle();
        toggle.textContent = 'Pauzeren';
        toggle.setAttribute('aria-pressed', 'true');
      } else {
        carousel.pause();
        carousel._config.interval = false;
        toggle.textContent = 'Afspelen';
        toggle.setAttribute('aria-pressed', 'false');
      }
    });
  }
});

9.4 Afbeeldingen — snel goed

  • Responsief: gebruik <picture> + webp; geef width/height mee → stabiele lay-out.
  • Lazy: zet loading="lazy" op níet-eerste dia’s.
  • Tekst als HTML (niet in de afbeelding) → leesbaar/SEO.

Snel testen

  • Autoplay staat uit (knop toont “Afspelen”).
  • Pijltjes, toetsenbord (← →) en swipe werken.
  • Status “Dia X van N” verandert mee.

Checklist

  • Geen autoplay: data-bs-ride="false" en interval: false.
  • Toegankelijkheid: labels op indicators, aria-live status, controls met Visually hidden-tekst.
  • Images: responsive + lazy; caption leesbaar (glaseffect).