Carousel
A horizontally scrollable sequence of images, supporting both JavaScript plugin control and native CSS carousel features.
The carousel component arranges content horizontally in a scrollable view with a "Previous" and "Next" scroll buttons and pagination markers. It can be used with custom JavaScript, initialized with the Honeycomb Carousel plugin, or rendered using modern CSS-only carousel, depending on your audience.
In the image examples we use height: 300px on the items list to limit how big the carousels look in the examples. That is not required, and you can remove it to let your images dictate the overall size of the component, or set a different value that looks better on your use case, it's up to you.
<section id="flix-carousel-01" aria-label="Flix Carousel of Amazing Images" class="flix-carousel" data-carousel>
<ul tabindex="0" 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(60deg) 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(120deg) brightness(80%) saturate(420%)" />
</li>
</ul>
</section>Modifiers
-
flix-carousel--no-buttons - Hides the previous and next scroll buttons in both CSS-only and plugin-managed carousels.
-
flix-carousel--no-markers - Hides the pagination scroll markers in both CSS-only and plugin-managed carousels.
-
flix-carousel--css-only -
Enables native CSS carousel styling using CSS pseudo-elements (
::scroll-button,::scroll-marker-group, and::scroll-marker) without requiring JavaScript-rendered control elements.
Plugin controlled
We recommend using the Honeycomb Carousel Plugin, you only need to provide the carousel container and items list and the appropriate labels for the controllers. The plugin automatically detects browser support for native CSS carousel pseudo-elements:
- If supported, it applies the
flix-carousel--css-onlymodifier and sets a unique--flix-carousel-anchor-namebased on the carouselid, so multiple carousels do not collide in CSS anchor positioning. - If not supported, it will:
- dynamically generate the controls (
<nav class="flix-carousel__controls">); - generate an
idfor.flix-carousel__itemsbased on the component's id, unless one is already present; - set up accessibility attributes (
aria-controls,aria-current,tabindex); - wire up scroll buttons, markers and keyboard navigation handlers.
- dynamically generate the controls (
Here is a simple step by step to configure the carousel to use the plugin:
- Add
data-carouselto the carousel section to initialize it. - Required: give the section a unique
idso the plugin can generate the listidand a unique CSS anchor name when needed. - Add
data-carousel-prev-labelanddata-carousel-next-labelto provide previous and next button labels for this carousel. - Add
data-carousel-controls-labelto provide controls landmark label for this carousel. - Add
data-carousel-marker-labelto each item for its marker button label.
You can also configure the labels in a shared config, please check the plugin documentation for all available options.
Formatting images
You can add anything inside the carousel items, the examples mostly show images but you can use image groups to organize multiple images inside the same pane.
<section id="flix-carousel-02" aria-label="Flix Carousel of Amazing Images" class="flix-carousel" data-carousel>
<ul id="flix-carousel-02-items" tabindex="0" 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">
<div class="flix-img-group flix-img-group--vertical">
<div class="flix-img-group__item">
<img class="flix-img-group__img" src="/img/img-placeholder-grey.png" alt="Sample image 1" style="filter: sepia(100%) hue-rotate(60deg) brightness(80%) saturate(420%)">
</div>
</div>
</li>
<li class="flix-carousel__item" data-carousel-marker-label="Go to item 2">
<div class="flix-img-group">
<div class="flix-img-group__item">
<img class="flix-img-group__img" src="/img/img-placeholder-grey.png" alt="Sample image 2" style="filter: sepia(100%) hue-rotate(120deg) brightness(80%) saturate(420%)">
</div>
<div class="flix-img-group__item">
<img class="flix-img-group__img" src="/img/img-placeholder-grey.png" alt="Sample image 3" style="filter: sepia(100%) hue-rotate(180deg) brightness(80%) saturate(420%)">
</div>
</div>
</li>
<li class="flix-carousel__item" data-carousel-marker-label="Go to item 3">
<div class="flix-img-group flix-img-group--vertical">
<div class="flix-img-group__item">
<img class="flix-img-group__img" src="/img/img-placeholder-grey.png" alt="Sample image 4" style="filter: sepia(100%) hue-rotate(240deg) brightness(80%) saturate(420%)">
</div>
<div class="flix-img-group__item">
<img class="flix-img-group__img" src="/img/img-placeholder-grey.png" alt="Sample image 5" style="filter: sepia(100%) hue-rotate(300deg) brightness(80%) saturate(420%)">
</div>
<div class="flix-img-group__item">
<img class="flix-img-group__img" src="/img/img-placeholder-grey.png" alt="Sample image 6" style="filter: sepia(100%) hue-rotate(0deg) brightness(80%) saturate(420%)">
</div>
</div>
</li>
<li class="flix-carousel__item" data-carousel-marker-label="Go to item 4">
<div class="flix-img-group">
<div class="flix-img-group__item">
<img class="flix-img-group__img" src="/img/img-placeholder-grey.png" alt="Sample image 7" style="filter: sepia(100%) hue-rotate(60deg) brightness(80%) saturate(420%)">
</div>
</div>
</li>
<li class="flix-carousel__item" data-carousel-marker-label="Go to item 5">
<div class="flix-img-group flix-img-group--vertical">
<div class="flix-img-group__item">
<img class="flix-img-group__img" src="/img/img-placeholder-grey.png" alt="Sample image 8" style="filter: sepia(100%) hue-rotate(120deg) brightness(80%) saturate(420%)">
</div>
<div class="flix-img-group__item">
<img class="flix-img-group__img" src="/img/img-placeholder-grey.png" alt="Sample image 9" style="filter: sepia(100%) hue-rotate(180deg) brightness(80%) saturate(420%)">
</div>
</div>
</li>
<li class="flix-carousel__item" data-carousel-marker-label="Go to item 6">
<div class="flix-img-group">
<div class="flix-img-group__item">
<img class="flix-img-group__img" src="/img/img-placeholder-grey.png" alt="Sample image 10" style="filter: sepia(100%) hue-rotate(240deg) brightness(80%) saturate(420%)">
</div>
<div class="flix-img-group__item">
<img class="flix-img-group__img" src="/img/img-placeholder-grey.png" alt="Sample image 11" style="filter: sepia(100%) hue-rotate(300deg) brightness(80%) saturate(420%)">
</div>
<div class="flix-img-group__item">
<img class="flix-img-group__img" src="/img/img-placeholder-grey.png" alt="Sample image 12" style="filter: sepia(100%) hue-rotate(0deg) brightness(80%) saturate(420%)">
</div>
</div>
</li>
</ul>
</section>Another option is to use image boxes inside the items.
Here we removed the height from the items list because we want to let the image boxes control the height of the carousel.
<section id="flix-carousel-03" aria-label="Flix Carousel of Amazing Images" class="flix-carousel" data-carousel>
<ul id="flix-carousel-03-items" tabindex="0" 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">
<a class="flix-img-box" href="#">
<div class="flix-img-box__img-wrapper">
<img class="flix-img-box__img" src="/img/img-placeholder-color.png" alt="Sample 1">
</div>
<div class="flix-img-box__content"><h3 class="flix-h3 flix-h3--section-header">Destination 1</h3><p class="flix-text">from $22.00</p></div>
</a>
</li>
<li class="flix-carousel__item" data-carousel-marker-label="Go to item 2">
<a class="flix-img-box" href="#">
<div class="flix-img-box__img-wrapper">
<img class="flix-img-box__img" src="/img/img-placeholder-color.png" alt="Sample 2">
</div>
<div class="flix-img-box__content"><h3 class="flix-h3 flix-h3--section-header">Destination 2</h3><p class="flix-text">from $22.00</p></div>
</a>
</li>
<li class="flix-carousel__item" data-carousel-marker-label="Go to item 3">
<a class="flix-img-box" href="#">
<div class="flix-img-box__img-wrapper">
<img class="flix-img-box__img" src="/img/img-placeholder-color.png" alt="Sample 3">
</div>
<div class="flix-img-box__content"><h3 class="flix-h3 flix-h3--section-header">Destination 3</h3><p class="flix-text">from $22.00</p></div>
</a>
</li>
<li class="flix-carousel__item" data-carousel-marker-label="Go to item 4">
<a class="flix-img-box" href="#">
<div class="flix-img-box__img-wrapper">
<img class="flix-img-box__img" src="/img/img-placeholder-color.png" alt="Sample 4">
</div>
<div class="flix-img-box__content"><h3 class="flix-h3 flix-h3--section-header">Destination 4</h3><p class="flix-text">from $22.00</p></div>
</a>
</li>
<li class="flix-carousel__item" data-carousel-marker-label="Go to item 5">
<a class="flix-img-box" href="#">
<div class="flix-img-box__img-wrapper">
<img class="flix-img-box__img" src="/img/img-placeholder-color.png" alt="Sample 5">
</div>
<div class="flix-img-box__content"><h3 class="flix-h3 flix-h3--section-header">Destination 5</h3><p class="flix-text">from $22.00</p></div>
</a>
</li>
</ul>
</section>No controls
You can disable the controls by adding their respective modifiers to the carousel section element:
flix-carousel--no-buttonsflix-carousel--no-markers
The same classes drive both the CSS-Only and JS-enhanced versions, so the plugin will check for the presence of these modifiers and not render the controls you want to hide.
When all controls are disabled, navigation relies entirely on touch swiping or scrolling through the container with the keyboard.
Without buttons you don't need to provide the data-carousel-prev-label and the data-carousel-next-label attributes.
Flix Carousel Without Scroll Buttons
<section id="flix-carousel-04" aria-labelledby="flix-carousel-04-label" class="flix-carousel flix-carousel--no-buttons" data-carousel>
<h2 id="flix-carousel-04-label" class="flix-h2 flix-h2--section-header">Flix Carousel Without Scroll Buttons</h2>
<ul tabindex="0" 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(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(60deg) 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(120deg) brightness(80%) saturate(420%)" />
</li>
</ul>
</section>Without markers you don't need to provide the data-carousel-marker-label for each item.
Flix Carousel Without Item Markers
-
Option 1
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est.
-
Option 2
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est.
-
Option 3
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est.
<section id="flix-carousel-05" aria-labelledby="flix-carousel-05-label" class="flix-carousel flix-carousel--no-markers" data-carousel>
<h2 id="flix-carousel-05-label" class="flix-h2 flix-h2--section-header">Flix Carousel Without Item Markers</h2>
<ul tabindex="0" 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">
<div class="flix-box flix-box--neutral">
<h3 class="flix-h3 flix-h3--section-header">Option 1</h3>
<div class="flix-text">
<p>
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit
amet quam egestas semper. Aenean ultricies mi vitae est.
</p>
</div>
</div>
</li>
<li class="flix-carousel__item">
<div class="flix-box flix-box--warning">
<h3 class="flix-h3 flix-h3--section-header">Option 2</h3>
<div class="flix-text">
<p>
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit
amet quam egestas semper. Aenean ultricies mi vitae est.
</p>
</div>
</div>
</li>
<li class="flix-carousel__item">
<div class="flix-box flix-box--success">
<h3 class="flix-h3 flix-h3--section-header">Option 3</h3>
<div class="flix-text">
<p>
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit
amet quam egestas semper. Aenean ultricies mi vitae est.
</p>
</div>
</div>
</li>
</ul>
</section>CSS-only carousel
When using the flix-carousel--css-only modifier without the plugin, the carousel utilizes modern browser native CSS carousel.
Because CSS anchor names are global, you must set a unique custom property on the carousel container, we recommend you use the same name as the ide, for consistency.
For example: --flix-carousel-anchor-name: --flix-carousel-06 is the anchor name of the next example, it has id="flix-carousel-06".
The plugin does this automatically when it initializes the component, but a manually CSS-only carousel must define it itself.
You still need to provide certain labels that the component CSS will use as alternative content for controls:
data-carousel-prev-label: Accessible label for the previous button, set onul.flix-carousel__items.data-carousel-next-label: Accessible label for the next button, set onul.flix-carousel__items.data-carousel-marker-label: Accessible label for each marker button, set on eachli.flix-carousel__item.
<section id="flix-carousel-06" aria-label="Modern CSS Powered Flix Carousel" class="flix-carousel flix-carousel--css-only" style="--flix-carousel-anchor-name: --flix-carousel-06;">
<ul tabindex="0" style="height: 300px;" class="flix-carousel__items" data-carousel-prev-label="Previous" data-carousel-next-label="Next">
<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(60deg) 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(120deg) brightness(80%) saturate(420%)" />
</li>
</ul>
</section>Full HTML markup
If you want to implement your own custom JavaScript logic or server-render the controls without using the plugin, this is the complete HTML markup you will need to reference to.
This includes the items list and the carousel controls containing the previous/next buttons and the marker group list.
Heading or label
Labeling the carousel can be done in two ways:
- If you want a visible heading, add one heading element with an
ideither inside the section or next to it, then associate it with the component usingaria-labelledby="{HEADING_ID}" - If you don't want or need a visible heading, you can simply label the section with
aria-labelwith an accessible and translated label, e.g.:aria-label="Carousel of destination ideas".
Items list
The items list (flix-carousel__items) is the most important element, and where most of the magic happens.
- This is the view with the scroll that allows users to swipe right and left, so it is important that you add
tabindex="0"to allow people to scroll using the keyboard arrow keys. - The items list also needs an
id="{LIST_ID}"so you can connect the "Previous" and "Next" scroll buttons witharia-controls="{LIST-ID}". - Similarly, each marker also controls the scroll of the items list, so they should also have
aria-controls="{LIST-ID}". - The carousel
widthandheightwill expand to fill the available space, and also respect the size and proportion of the images in it. If you want to control the height of the items, set the height on the list of items so the images inside will respect that height.
Controls navigation landmark
It is important to add a label to the navigation landmark that wraps the controls, so screen reader users can easily identify and navigate to it.
Use aria-label with a short, clear and properly translated label. E.g.: "Carousel controls", "Controles do Carrossel".
Scroll buttons
The scroll buttons move the carousel right or left and should have the following attributes:
aria-controls="{LIST-ID}"to inform screen readers of the relationship between these elements.aria-label: Accessible label properly translated to identify "Previous" and "Next" buttons.disabledattribute present if the carousel is at the first or last item.
Scroll markers
The marker group is an ordered list where each individual item represents one carousel item, and the markers and items are associated via aria-label.
The marker buttons should have the following attributes:
aria-controls="{LIST-ID}"to inform screen readers of the relationship between these elements.aria-labelwith an accessible and translated label informing which item the marker makes the carousel scroll to. E.g.: "Go to item 1".aria-current="true"andtabindex="0"Only on the marker that represents the currently visible item.tabindex="-1"On the remaining markers.
Flix Carousel of Amazing Images
<section id="flix-carousel-07" aria-labelledby="flix-carousel-07-label" class="flix-carousel">
<h2 id="flix-carousel-07-label" class="flix-h2 flix-h2--section-header">Flix Carousel of Amazing Images</h2>
<ul id="flix-carousel-07-items" tabindex="0" style="height: 300px;" class="flix-carousel__items">
<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(0deg) 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(60deg) 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(120deg) brightness(80%) saturate(420%)" />
</li>
</ul>
<nav class="flix-carousel__controls" aria-label="Carousel controls">
<button class="flix-carousel__scroll-button flix-carousel__scroll-button--prev" aria-controls="flix-carousel-07-items" aria-label="Previous">
</button>
<ol class="flix-carousel__scroll-marker-group">
<li>
<button
type="button"
class="flix-carousel__scroll-marker"
aria-controls="flix-carousel-07-items"
aria-label="Go to item 1"
aria-current="true"
tabindex="0"
></button>
</li>
<li>
<button
type="button"
class="flix-carousel__scroll-marker"
aria-controls="flix-carousel-07-items"
aria-label="Go to item 2"
tabindex="-1"
></button>
</li>
<li>
<button
type="button"
class="flix-carousel__scroll-marker"
aria-controls="flix-carousel-07-items"
aria-label="Go to item 3"
tabindex="-1"
></button>
</li>
</ol>
<button class="flix-carousel__scroll-button flix-carousel__scroll-button--next" aria-controls="flix-carousel-07-items" aria-label="Next">
</button>
</nav>
</section>