Vai al contenuto

Componente

Carousel

Caroselli di card o immagini su daisyUI carousel (scroll-snap). Pulsanti avanti/indietro e punti di navigazione sono pseudo-elementi CSS (::scroll-button, ::scroll-marker): nessuno script.

Riferimento
<it-carousel>, <it-carousel-item>
Classi
ita-carouselita-carousel-imageita-carousel-peekita-carousel-captionita-carousel-titleita-carousel-pager
Basato su (daisyUI)
carouselcarousel-itemcard
Estensioni
it-carousel
Solo CSS
Lo scorrimento è nativo (scroll-snap): swipe, trackpad, Maiusc+rotella e frecce da tastiera sul contenitore a fuoco. it-carousel aggiunge pulsanti e punti con ::scroll-button e ::scroll-marker (Chrome 135+); negli altri browser resta lo scorrimento, oppure usa controls: "links" con link ad ancora, che funzionano ovunque ma cambiano l'hash e possono scorrere la pagina. Autoplay e pulsanti per Safari/Firefox richiedono JavaScript: vedi sotto.
Storybook
Apri le storie di Carousel

Carosello di card

Mostra il codice HTML
<section class="ita-carousel" aria-roledescription="carosello" aria-labelledby="carousel-1-title">
  <h2 id="carousel-1-title" class="ita-carousel-title">Notizie in evidenza</h2>
  <div>
    <div class="carousel it-carousel" tabindex="0">
    <div id="carousel-1-1" role="group" aria-roledescription="slide" aria-label="1 di 5" class="carousel-item">
      <article class="card ita-card">
  <figure><img src="https://picsum.photos/id/1031/800/450" alt="Edificio comunale" loading="lazy"></figure>
  <div class="card-body">
    <h3 class="card-title">
      <a href="#">Nuovo sportello digitale</a>
    </h3>
    <p class="ita-card-text">Prenota un appuntamento con l'anagrafe direttamente online.</p>
    <footer class="ita-card-footer">
      <div><a href="#" class="ita-card-category">Servizi</a></div>
      <time>18 set 2026</time>
    </footer>
  </div>
</article>
    </div>
    <div id="carousel-1-2" role="group" aria-roledescription="slide" aria-label="2 di 5" class="carousel-item">
      <article class="card ita-card">
  <figure><img src="https://picsum.photos/id/1067/800/450" alt="Strada cittadina" loading="lazy"></figure>
  <div class="card-body">
    <h3 class="card-title">
      <a href="#">Mobilità sostenibile</a>
    </h3>
    <p class="ita-card-text">Quaranta nuove stazioni di bike sharing in tutta la città.</p>
    <footer class="ita-card-footer">
      <div><a href="#" class="ita-card-category">Mobilità</a></div>
      <time>12 set 2026</time>
    </footer>
  </div>
</article>
    </div>
    <div id="carousel-1-3" role="group" aria-roledescription="slide" aria-label="3 di 5" class="carousel-item">
      <article class="card ita-card">
  <figure><img src="https://picsum.photos/id/1073/800/450" alt="Aula scolastica" loading="lazy"></figure>
  <div class="card-body">
    <h3 class="card-title">
      <a href="#">Scuole aperte</a>
    </h3>
    <p class="ita-card-text">Iscrizioni ai servizi di mensa e trasporto fino al 30 settembre.</p>
    <footer class="ita-card-footer">
      <div><a href="#" class="ita-card-category">Scuola</a></div>
      <time>5 set 2026</time>
    </footer>
  </div>
</article>
    </div>
    <div id="carousel-1-4" role="group" aria-roledescription="slide" aria-label="4 di 5" class="carousel-item">
      <article class="card ita-card">
  <figure><img src="https://picsum.photos/id/1044/800/450" alt="Parco cittadino" loading="lazy"></figure>
  <div class="card-body">
    <h3 class="card-title">
      <a href="#">Estate in città</a>
    </h3>
    <p class="ita-card-text">Il programma completo degli eventi culturali nei parchi.</p>
    <footer class="ita-card-footer">
      <div><a href="#" class="ita-card-category">Cultura</a></div>
      <time>1 set 2026</time>
    </footer>
  </div>
</article>
    </div>
    <div id="carousel-1-5" role="group" aria-roledescription="slide" aria-label="5 di 5" class="carousel-item">
      <article class="card ita-card">
  <figure><img src="https://picsum.photos/id/1018/800/450" alt="Paesaggio verde" loading="lazy"></figure>
  <div class="card-body">
    <h3 class="card-title">
      <a href="#">Raccolta differenziata</a>
    </h3>
    <p class="ita-card-text">Cambiano i giorni di ritiro nei quartieri nord.</p>
    <footer class="ita-card-footer">
      <div><a href="#" class="ita-card-category">Ambiente</a></div>
      <time>28 ago 2026</time>
    </footer>
  </div>
</article>
    </div>
    </div>
  </div>
</section>

Immagini a tutta larghezza

Mostra il codice HTML
<section class="ita-carousel ita-carousel-image" aria-roledescription="carosello" aria-label="Galleria del territorio">
  <div>
    <div class="carousel it-carousel" tabindex="0">
    <figure id="carousel-2-1" role="group" aria-roledescription="slide" aria-label="1 di 5" class="carousel-item">
      <img src="https://picsum.photos/id/1031/1200/600" alt="Edificio comunale" loading="lazy">
      <figcaption class="ita-carousel-caption">
        <p>Nuovo sportello digitale</p>
        <p>Prenota un appuntamento con l'anagrafe direttamente online.</p>
      </figcaption>
    </figure>
    <figure id="carousel-2-2" role="group" aria-roledescription="slide" aria-label="2 di 5" class="carousel-item">
      <img src="https://picsum.photos/id/1067/1200/600" alt="Strada cittadina" loading="lazy">
      <figcaption class="ita-carousel-caption">
        <p>Mobilità sostenibile</p>
        <p>Quaranta nuove stazioni di bike sharing in tutta la città.</p>
      </figcaption>
    </figure>
    <figure id="carousel-2-3" role="group" aria-roledescription="slide" aria-label="3 di 5" class="carousel-item">
      <img src="https://picsum.photos/id/1073/1200/600" alt="Aula scolastica" loading="lazy">
      <figcaption class="ita-carousel-caption">
        <p>Scuole aperte</p>
        <p>Iscrizioni ai servizi di mensa e trasporto fino al 30 settembre.</p>
      </figcaption>
    </figure>
    <figure id="carousel-2-4" role="group" aria-roledescription="slide" aria-label="4 di 5" class="carousel-item">
      <img src="https://picsum.photos/id/1044/1200/600" alt="Parco cittadino" loading="lazy">
      <figcaption class="ita-carousel-caption">
        <p>Estate in città</p>
        <p>Il programma completo degli eventi culturali nei parchi.</p>
      </figcaption>
    </figure>
    <figure id="carousel-2-5" role="group" aria-roledescription="slide" aria-label="5 di 5" class="carousel-item">
      <img src="https://picsum.photos/id/1018/1200/600" alt="Paesaggio verde" loading="lazy">
      <figcaption class="ita-carousel-caption">
        <p>Raccolta differenziata</p>
        <p>Cambiano i giorni di ritiro nei quartieri nord.</p>
      </figcaption>
    </figure>
    </div>
  </div>
</section>

Con anteprima della slide successiva

Mostra il codice HTML
<section class="ita-carousel ita-carousel-peek" aria-roledescription="carosello" aria-label="Servizi">
  <div>
    <div class="carousel it-carousel" tabindex="0">
    <div id="carousel-3-1" role="group" aria-roledescription="slide" aria-label="1 di 5" class="carousel-item">
      <article class="card ita-card">
  <figure><img src="https://picsum.photos/id/1031/800/450" alt="Edificio comunale" loading="lazy"></figure>
  <div class="card-body">
    <h3 class="card-title">
      <a href="#">Nuovo sportello digitale</a>
    </h3>
    <p class="ita-card-text">Prenota un appuntamento con l'anagrafe direttamente online.</p>
    <footer class="ita-card-footer">
      <div><a href="#" class="ita-card-category">Servizi</a></div>
      <time>18 set 2026</time>
    </footer>
  </div>
</article>
    </div>
    <div id="carousel-3-2" role="group" aria-roledescription="slide" aria-label="2 di 5" class="carousel-item">
      <article class="card ita-card">
  <figure><img src="https://picsum.photos/id/1067/800/450" alt="Strada cittadina" loading="lazy"></figure>
  <div class="card-body">
    <h3 class="card-title">
      <a href="#">Mobilità sostenibile</a>
    </h3>
    <p class="ita-card-text">Quaranta nuove stazioni di bike sharing in tutta la città.</p>
    <footer class="ita-card-footer">
      <div><a href="#" class="ita-card-category">Mobilità</a></div>
      <time>12 set 2026</time>
    </footer>
  </div>
</article>
    </div>
    <div id="carousel-3-3" role="group" aria-roledescription="slide" aria-label="3 di 5" class="carousel-item">
      <article class="card ita-card">
  <figure><img src="https://picsum.photos/id/1073/800/450" alt="Aula scolastica" loading="lazy"></figure>
  <div class="card-body">
    <h3 class="card-title">
      <a href="#">Scuole aperte</a>
    </h3>
    <p class="ita-card-text">Iscrizioni ai servizi di mensa e trasporto fino al 30 settembre.</p>
    <footer class="ita-card-footer">
      <div><a href="#" class="ita-card-category">Scuola</a></div>
      <time>5 set 2026</time>
    </footer>
  </div>
</article>
    </div>
    <div id="carousel-3-4" role="group" aria-roledescription="slide" aria-label="4 di 5" class="carousel-item">
      <article class="card ita-card">
  <figure><img src="https://picsum.photos/id/1044/800/450" alt="Parco cittadino" loading="lazy"></figure>
  <div class="card-body">
    <h3 class="card-title">
      <a href="#">Estate in città</a>
    </h3>
    <p class="ita-card-text">Il programma completo degli eventi culturali nei parchi.</p>
    <footer class="ita-card-footer">
      <div><a href="#" class="ita-card-category">Cultura</a></div>
      <time>1 set 2026</time>
    </footer>
  </div>
</article>
    </div>
    <div id="carousel-3-5" role="group" aria-roledescription="slide" aria-label="5 di 5" class="carousel-item">
      <article class="card ita-card">
  <figure><img src="https://picsum.photos/id/1018/800/450" alt="Paesaggio verde" loading="lazy"></figure>
  <div class="card-body">
    <h3 class="card-title">
      <a href="#">Raccolta differenziata</a>
    </h3>
    <p class="ita-card-text">Cambiano i giorni di ritiro nei quartieri nord.</p>
    <footer class="ita-card-footer">
      <div><a href="#" class="ita-card-category">Ambiente</a></div>
      <time>28 ago 2026</time>
    </footer>
  </div>
</article>
    </div>
    </div>
  </div>
</section>

Con JavaScript (opzionale)

La ricetta è solo CSS e funziona così com'è. Questi frammenti non sono inclusi nel pacchetto: mostrano come aggiungere il comportamento che richiede uno script, in JavaScript semplice o in React.

Pulsanti per Safari e Firefoxjs

Aggiunge i pulsanti solo dove mancano quelli CSS.

// Prev/next buttons for browsers without ::scroll-button (Safari, Firefox).
// Skipped where the CSS buttons exist, so the two never show together.
if (!CSS.supports("selector(::scroll-button(*))")) {
  document.querySelectorAll(".it-carousel").forEach((track) => {
    const make = (dir, label, glyph) => {
      const b = document.createElement("button");
      b.type = "button";
      b.className = `ita-btn ita-btn-primary ita-btn-circle absolute top-1/2 -translate-y-1/2 ${dir < 0 ? "start-2" : "end-2"}`;
      b.setAttribute("aria-label", label);
      b.textContent = glyph;
      b.addEventListener("click", () => track.scrollBy({ left: dir * track.clientWidth }));
      return b;
    };
    track.parentElement.append(make(-1, "Precedente", "‹"), make(1, "Successivo", "›"));
  });
}

Autoplay accessibilejs

bootstrap-italia non lo fa di default: se ti serve, si ferma al passaggio del mouse, al focus e con «riduci movimento».

// Autoplay that stops on hover, on focus and with "reduce motion".
// Works with or without the CSS scroll buttons.
document.querySelectorAll("[aria-roledescription='carosello']").forEach((region) => {
  const track = region.querySelector(".carousel");
  if (!track || matchMedia("(prefers-reduced-motion: reduce)").matches) return;
  let paused = false;
  for (const [on, off] of [["mouseenter", "mouseleave"], ["focusin", "focusout"]]) {
    region.addEventListener(on, () => (paused = true));
    region.addEventListener(off, () => (paused = false));
  }
  setInterval(() => {
    if (paused) return;
    const atEnd = track.scrollLeft + track.clientWidth >= track.scrollWidth - 1;
    track.scrollTo({ left: atEnd ? 0 : track.scrollLeft + track.clientWidth });
  }, 6000);
});

Componente Reacttsx

import { useRef, type ReactNode } from "react";

/** The recipe's markup as a component; buttons use scrollBy, so they work in every browser. */
export function Carousel({ label, children }: { label: string; children: ReactNode[] }) {
  const track = useRef<HTMLDivElement>(null);
  const go = (dir: 1 | -1) => track.current?.scrollBy({ left: dir * track.current.clientWidth });
  return (
    <section className="ita-carousel" aria-roledescription="carosello" aria-label={label}>
      <div>
        <div ref={track} className="carousel" tabIndex={0}>
          {children.map((child, i) => (
            <div key={i} role="group" aria-roledescription="slide" aria-label={`${i + 1} di ${children.length}`}
                 className="carousel-item">
              {child}
            </div>
          ))}
        </div>
        <button type="button" className="ita-btn ita-btn-primary ita-btn-circle absolute start-2 top-1/2 -translate-y-1/2" aria-label="Precedente" onClick={() => go(-1)}>‹</button>
        <button type="button" className="ita-btn ita-btn-primary ita-btn-circle absolute end-2 top-1/2 -translate-y-1/2" aria-label="Successivo" onClick={() => go(1)}>›</button>
      </div>
    </section>
  );
}