Honeycomb

Carousel

The Carousel plugin manages accessible scroll buttons, pagination markers and keyboard navigation for the carousel component, with automatic fallback to native CSS carousel support.

The Carousel plugin enhances the carousel component markup with everything it needs to be fully interactive and accessible.

The plugin will take care of all of this for you:

  • Detect whether the browser supports native CSS carousel pseudo elements and applies the --css-only modifier accordingly, so no extra markup is generated when it's not needed;
  • When CSS carousel isn't supported, generate the previous/next scroll buttons and the pagination markers, with all the accessibility attributes wired up;
  • If CSS-only mode is used, generate a unique CSS anchor name from the carousel component id so multiple carousels can coexist without anchor collisions;
  • Keep the scroll buttons' disabled state and the current marker in sync with the visible item, whether navigation happens by clicking the controls, using the keyboard, or scrolling/swiping the items list directly.
  • Make the correct elements interactive by managing tabindex on the items list and individual item markers.

Add the plugin to your page

Include the plugin from our CDN at the end of your page and then initialize it with:

<script src="https://honeycomb.flixbus.com/dist/{VERSION_TAG}/js/carousel.js"></script>
<script>
  document.addEventListener("DOMContentLoaded", function() {
    carousel.init();
  });
</script>

Configure shared labels

Shared translated labels can be configured in the arguments of carousel.init().

Individual data-carousel-*-label attributes override these options for specific cases when needed.

carousel.init({
  prevLabel: 'Previous',
  nextLabel: 'Next',
  controlsLabel: 'Carousel controls',
  markerLabel: 'Go to item {i}',
});

Use the {i} placeholder in markerLabel to insert the 1-based index of each item.

Configure the plugin with data attributes

data-carousel
Where: section.flix-carousel
Required. Identifies the carousel container so the plugin can initialize it.
id
Where: section.flix-carousel
Required. Use a unique identifier for the carousel container so the plugin can generate a unique CSS anchor name and derive the items list id when needed.
data-carousel-prev-label
Where: ul.flix-carousel__items
Default: "Previous"
Overrides the shared prevLabel configuration for this carousel.
data-carousel-next-label
Where: ul.flix-carousel__items
Default: "Next"
Overrides the shared nextLabel configuration for this carousel.
data-carousel-controls-label
Where: ul.flix-carousel__items
Default: "Carousel controls"
Overrides the shared controlsLabel configuration for this carousel.
data-carousel-marker-label
Where: li.flix-carousel__item
Default: "Go to item {i}", where {i} is replaced with the 1-based item index.
Overrides the shared markerLabel template configuration for this marker button.
data-carousel-css-only
Where: section.flix-carousel
Optional and situational. Set to "true" or "false" to force CSS-only or JavaScript-controlled mode respectively, bypassing automatic feature detection.

The fallback values are present to provide a bare-minimum label for the controls, but they are not translated. Please provide properly translated accessible labels for the controls using the plugin init configuration or the inline data attributes.

<section id="flix-carousel-example" aria-label="Flix Carousel of Amazing Images" class="flix-carousel" data-carousel>
  <ul style="height: 300px;" class="flix-carousel__items" data-carousel-prev-label="Previous" data-carousel-next-label="Next" data-carousel-controls-label="Carousel controls">
    <li class="flix-carousel__item" data-carousel-marker-label="Go to item 1">
      <img src="/img/img-placeholder-grey.png" alt="Sample image 1" width="840" height="640" style="filter: sepia(100%) hue-rotate(0deg) brightness(80%) saturate(420%)" />
    </li>
    <li class="flix-carousel__item" data-carousel-marker-label="Go to item 2">
      <img src="/img/img-placeholder-grey.png" alt="Sample image 2" width="840" height="640" style="filter: sepia(100%) hue-rotate(30deg) brightness(80%) saturate(420%)" />
    </li>
    <li class="flix-carousel__item" data-carousel-marker-label="Go to item 3">
      <img src="/img/img-placeholder-grey.png" alt="Sample image 3" width="840" height="640" style="filter: sepia(100%) hue-rotate(60deg) brightness(80%) saturate(420%)" />
    </li>
  </ul>
</section>

Disabling specific controls

Add the flix-carousel--no-buttons and/or flix-carousel--no-markers modifier classes to the section to hide the previous/next scroll buttons and/or the pagination markers.

The plugin reads these same classes, so you don't need any extra data attribute to keep them in sync and your component API will be future proof if the component becomes CSS-Only.

Without buttons you don't need to provide the data-carousel-prev-label and data-carousel-next-label attributes:

<section id="flix-carousel-example-no-buttons" aria-label="Flix Carousel of Amazing Images" class="flix-carousel flix-carousel--no-buttons" data-carousel>
  <ul style="height: 300px;" class="flix-carousel__items" data-carousel-controls-label="Carousel controls">
    <li class="flix-carousel__item" data-carousel-marker-label="Go to item 1">
      <img src="/img/img-placeholder-grey.png" alt="Sample image 1" width="840" height="640" style="filter: sepia(100%) hue-rotate(180deg) brightness(80%) saturate(420%)" />
    </li>
    <li class="flix-carousel__item" data-carousel-marker-label="Go to item 2">
      <img src="/img/img-placeholder-grey.png" alt="Sample image 2" width="840" height="640" style="filter: sepia(100%) hue-rotate(210deg) brightness(80%) saturate(420%)" />
    </li>
    <li class="flix-carousel__item" data-carousel-marker-label="Go to item 3">
      <img src="/img/img-placeholder-grey.png" alt="Sample image 3" width="840" height="640" style="filter: sepia(100%) hue-rotate(240deg) brightness(80%) saturate(420%)" />
    </li>
  </ul>
</section>

Without markers you don't need to provide the data-carousel-marker-label attribute on each item:

<section id="flix-carousel-example-no-markers" aria-label="Flix Carousel of Amazing Images" class="flix-carousel flix-carousel--no-markers" data-carousel>
  <ul style="height: 300px;" class="flix-carousel__items" data-carousel-prev-label="Previous" data-carousel-next-label="Next" data-carousel-controls-label="Carousel controls">
    <li class="flix-carousel__item">
      <img src="/img/img-placeholder-grey.png" alt="Sample image 1" width="840" height="640" style="filter: sepia(100%) hue-rotate(270deg) brightness(80%) saturate(420%)" />
    </li>
    <li class="flix-carousel__item">
      <img src="/img/img-placeholder-grey.png" alt="Sample image 2" width="840" height="640" style="filter: sepia(100%) hue-rotate(300deg) brightness(80%) saturate(420%)" />
    </li>
    <li class="flix-carousel__item">
      <img src="/img/img-placeholder-grey.png" alt="Sample image 3" width="840" height="640" style="filter: sepia(100%) hue-rotate(330deg) brightness(80%) saturate(420%)" />
    </li>
  </ul>
</section>

Disabling both controls means that navigation relies entirely on touch swiping or scrolling through the items list with the keyboard.

Forcing CSS-only or JavaScript-controlled mode

By default the plugin detects browser support for native CSS carousel automatically. If you need to bypass that check, add data-carousel-css-only to the section set to "true" or "false".

Forcing CSS-only applies the flix-carousel--css-only modifier without generating any controls.

Forcing JavaScript-controlled mode always generates the controls, even in browsers that support native CSS carousel.

This is useful if browsers start adopting the same support as Chrome and Edge, but the Honeycomb component still doesn't support a given browser.

<section id="flix-carousel-js" aria-label="Flix Carousel of Amazing Images" class="flix-carousel" data-carousel data-carousel-css-only="false" style="--flix-carousel-anchor-name: --flix-carousel-js;">
  <ul style="height: 300px;" class="flix-carousel__items" data-carousel-prev-label="Previous" data-carousel-next-label="Next" data-carousel-controls-label="Carousel controls">
    <li class="flix-carousel__item" data-carousel-marker-label="Go to item 1">
      <img src="/img/img-placeholder-grey.png" alt="Sample image 1" width="840" height="640" style="filter: sepia(100%) hue-rotate(90deg) brightness(80%) saturate(420%)" />
    </li>
    <li class="flix-carousel__item" data-carousel-marker-label="Go to item 2">
      <img src="/img/img-placeholder-grey.png" alt="Sample image 2" width="840" height="640" style="filter: sepia(100%) hue-rotate(120deg) brightness(80%) saturate(420%)" />
    </li>
    <li class="flix-carousel__item" data-carousel-marker-label="Go to item 3">
      <img src="/img/img-placeholder-grey.png" alt="Sample image 3" width="840" height="640" style="filter: sepia(100%) hue-rotate(150deg) brightness(80%) saturate(420%)" />
    </li>
  </ul>
</section>