carousel-kit

A series of web components that can be composed to create any manner of carousel. Each component manages its own state and exposes it through JavaScript events, HTML attributes, and custom CSS properties.

The dot pattern background of this page is made up of .

<slotted-carousel>
  <timer-normalized
    slot="timer"
    duration="5">
  </timer-normalized>
  <carousel-track
    slot="track>
    <carousel-slide
      slot="slide">
      <-- slide -->
    </carousel-slide>
  </carousel-track>
  <carousel-progress
    slot="progress>
    <progress-indicator
      slot="indicator">
    </progress-indicator>
  </carousel-progress>
  <button
    slot="previous">
  </button>
  <button
    slot="next">
  </button>
</slotted-carousel>
<style>
carousel-slide {
 --slide-offset:
  min(
   var(--previous-active-offset),
   var(--next-active-offset));
}
</style>

Style agonostic

Use the web component's exposed state to write modern CSS to handle layout and transitions. Transition left to right, right to left, top to bottom, bottom to top, each is achievable with the same core web components.

No build step

Lean on script module imports to define custom web components and carry on writing and configuring your carousels purely in HTML and CSS. Most carousels will require no additional Javascript to be written, but there are affordances to extend and integrate with other elements if it's desireable.

<-- define each web component -->
<script src="https://esm.run/carousel-kit@1/define-slotted-carousel" type="module"></script>
<script src="https://esm.run/carousel-kit@1/define-carousel-track" type="module"></script>
<script src="https://esm.run/carousel-kit@1/define-carousel-slide" type="module"></script>
<script src="https://esm.run/carousel-kit@1/define-carousel-progress" type="module"></script>
<script src="https://esm.run/carousel-kit@1/define-progress-indicator" type="module"></script>
<script src="https://esm.run/carousel-kit@1/define-timer-normalized" type="module"></script>

<-- or define them all -->
<script src="https://esm.run/carousel-kit@1/define-all" type="module"></script>

Learn by example

Check out the examples below to see how to accomplish common carousel patterns. The examples below are displayed within a series of carousel-kit components, which are styled with inline CSS just below this element in the source code for clarity as to how it works. This functionality is a bit closer to a tab display, but that is a side effect of element composition and styling. Each example is an iframe and below it there is a link to the source code to understand how each example is composed in isolation. The source code includes comments that point you to the specifics that differentiates each example.

centered slide order attributes

centered slotted carousel order attribute

centered too few slides

centered touch swipeable vertical

centered touch swipeable

centered

one-up progress

one-up

advance on timer with pause resume

advance on timer resets interval on keyboard navigation (arrow left + right)

two-up

example page to view source

example page to view source

example page to view source

example page to view source

example page to view source

example page to view source

example page to view source

example page to view source

example page to view source

example page to view source

example page to view source

API Reference

Below each web component is represented, and its HTML attributes, CSS properties, and JavaScript events are outlined.

<slotted-carousel>

<-- hideshow slotted-caoursel docs -->

HTML Attributes

start-active="integer" can be set with an integer value that represents the first slide to be active. For example, if you have 5 slides in your carousel, and you want the 3rd slide to be active on load, you could set start-active="3" on your <slotted-carousel>.

random-order add this attribute to a <slotted-carousel> to show your [slot="slide"] elements in a random order. This random order will also be reflected in the progress indicator ([slot="indicator"]) elements, so that your slides and progress indicators show in the same order.

order='[{ "originalIndex": integer, "newIndex": integer }]' add this attribute to a <slotted-carousel> to show your [slot="slide"] elements in a custom order. This custom order will also be reflected in the progress indicator ([slot="indicator"]) elements, so that your slides and progress indicators show order the same order. The value of the attribute should be JSON stringified value of an array that includes objects with two keys, originalIndex which should be the slide's original position in the sequence of slides (0 indexed), and newIndex which reflects the final position in the sequence of slides (also 0 indexed).

previous-key="keyCode"
next-key="keyCode" are used to define keyboard navigation. The value of these attributes should be an event.key value from a keydown event on the <slotted-carousel>. For example, to use the arrow left key to go to the previous slide, and the arrow right key to go to the next slide, you could set these values as previous-key="ArrowLeft" next-key="ArrowRight".

expose-timer add this attribute to a <slotted-carousel> element that has a <timer-normalized slot="timer"> element as its child, and the <slotted-carousel> will have a CSS property --timer-normalized with a floating point value from 0-1 of the timer's current progress through its duration.

Behind the scenes the <slotted-carousel> is listening for tick events from its [slot="timer"] child which should contain an event.detail.interval value to echo into the --timer-normalized CSS property.

expose-slide-count add this attribute to a <slotted-carousel> to have the CSS property --slide-count set as an integer value on the carousel element. This can be useful for determining z-index values for your slides, or otherwise bounding the value space for CSS properties that lean on the slide count.

expose-active-slide add this attribute to a <slotted-carousel> to have the CSS property --active-slide set as an integer value on the carousel element.

touch-swipeable add this attribute to a <slotted-carousel> to enable touch events for changing the active slide. Be default, swiping left will trigger the next slide method, swiping right will tiger the previous slide method. Use the swipe-{up,down,left-right}-action attribute to modify this default behavior.

swipe-up-action
swipe-down-action
swipe-left-action
swipe-right-action attributes can be set to "next" or "previous" to map that swipe direction to the next or previous slide method.

CSS properties

--slide-count CSS property will be added to any <slotted-carousel> with the expose-slide-count attribute set on it.

--active-slide CSS property will be added to any <slotted-carousel> with the expose-active-slide attribute set on it.

--timer-normalized CSS property will be added to any <slotted-carousel> with the expose-timer attribute set on it, that also contains a [slot="timer"] child.

JavaScript Events

ready event is emitted once the connectedCallback is done running. This is where all event listeners are set up, after this event fires the element is ready to do everything that it does.

slide event is emitted at the end of the nextSlide or previousSlide method execution. The carousel.state object value, which includes the activeSlide and maxSlideIndex will exist on event.detail.

</slotted-carousel>

<carousel-track>

<-- hideshow carousel-track docs -->

HTML Attributes

slot="track" apply this attribute to your <carousel-track> to have the parent carousel keep track of it properly. This is based on the default selector values for the SlottedCarousel, change the selector in your SlottedCarousel in coordination with how you define your <carousel-track>.

minimum-slide-count="integer" can be set to an integer value on a <carousel-track> to duplicate the set of slides in the track until the minimum count is reached. This is useful if you have fewer slides than necessary to make your transitions work. For example, if you have a 3-up carousel, and only 3 slides, you will want to a buffer slide on each end to transition in and out of, so you might set the minimum slide count to 5.

carousel-query optionally set this to the string you would prefer to use to find a carousel parent. The default value is slotted-carousel.

item-query optionally set this to the string you would prefer to use to find a carousel slides. The default value is carousel-slide.

</carousel-track>

<carousel-slide>

<-- hideshow carousel-slide docs -->

HTML Attributes

slot="slide" apply this attribute to your <carousel-slide> to have the parent carousel keep track of it properly. This is based on the default selector values for the SlottedCarousel, change the selector in your SlottedCarousel in coordiation with how you define your <carousel-slide>.

order="integer" can be set to an integer value on a <carousel-slide> to determine the order of the slide in the track. This is an optional attribute, if it is set on one slide, it should be set on all slides, each with a unique value. Omitting this attribute will lead to the default DOM order being used. It is also possible to use the random-order or order attributes on the <slotted-carousel> to determine slide + indicator order.

active
previous-active
next-active attributes are mutually exclusive, every <carousel-slide> will only have one of these at a time. active will be available when the carousel state has this slide's index as the active one. previous-active when the shortest path between the active slide and this one is through subtraction, with the attribute value set to the offset from the active index. next-active when the shortest path between the active slide and this one is through addition, with the attribute value set to the offset from the active index. For example, if the slide index is 3, and the current active slide index is 0, and there are 10 slides total, the slide would have the attribute next-active="2".

CSS Properties

--previous-active-offset
--next-active-offset properties are set for every <carousel-slide> to reflect the distance between the slide's index value and the active slide index value. When the current slide is active, both of these values are set to 0. It can be useful to use the min() CSS funciton to give you the shortest path to your slide.

</carousel-slide>

<carousel-progress>

<-- hideshow carousel-progress docs -->

HTML Attributes

slot="progress" apply this attribute to your <carousel-progress> to have the parent carousel keep track of it properly. This is based on the default selector values for the SlottedCarousel, change the selector in your SlottedCarousel in coordiation with how you define your <carousel-progress>.

click-set-active-slide apply this attribute to your <carousel-progress> if you would like to attach click handlers to the progress indicators to transition the carousel's active slide to the match the progress indicator clicked.

</carousel-progress>

<progress-indicator>

<-- hideshow progress-indicator docs -->

HTML Attributes

slot="indicator" apply this attribute to your <progress-indicator> to have the parent carousel keep track of it properly. This is based on the default selector values for the SlottedCarousel, change the selector in your SlottedCarousel in coordiation with how you define your <progress-indicator>.

order="integer" can be set to an integer value on a <progress-indicator> to determine the order of the indicator in the progression. This is an optional attribute, if it is set on one indicator, it should be set on all indicators, each with a unique value. Omitting this attribute will lead to the default DOM order being used. It is also possible to use the random-order or order attributes on the <slotted-carousel> to determine slide + indicator order.

active
previous-active
next-active attributes are mutually exclusive, every <progress-indicator> will only have one of these at a time. active will be available when the carousel state has this slide's index as the active one. previous-active when the shortest path between the active slide and this one is through subtraction, with the attribute value set to the offset from the active index. next-active when the shortest path between the active slide and this one is through addition, with the attribute value set to the offset from the active index. For example, if the slide index is 3, and the current active slide index is 0, and there are 10 slides total, the slide would have the attribute next-active="2".

CSS Properties

--previous-active-offset
--next-active-offset properties are set for every <progress-indicator> to reflect the distance between the slide's index value and the active slide index value. When the current slide is active, both of these values are set to 0. It can be useful to use the min() CSS funciton to give you the shortest path to your slide.

</progress-indicator>

<timer-normalized>

<-- hideshow timer-normalized docs -->

HTML Attributes

slot="timer" apply this attribute to your <timer-normalized> to have the parent carousel keep track of it properly. This is based on the default selector values for the SlottedCarousel, change the selector in your SlottedCarousel in coordiation with how you define your <timer-normalized>.

duration represents the number of seconds that the timer normalizes will account for as its interval. If the duration is set to 10, the timer will emit 0.5 at 5 seconds and 1.0 at 10 seconds.

offset optionally set this to the float value in seconds that the duration should be offset by. This is useful when there are multiple timer-based carousels, and you want them to be offset by some amount.

JavaScript Events

tick event is emitted using the requestAnimationFrame API, with the state of the timer stored at event.detail. This includes interval value, a float from 0 - 1 representing the progress through the duration. start is also included, which is the internal Date.now() value associated with the timer. wrapCount is also included, which is the count of how many times the timer has lapsed its duration. Internally the SlottedCarousel tracks this to understand when to auto-advance.

ready event is emitted once the connectedCallback is done running. This is where all event listeners are set up, after this event fires the element is ready to do everything that it does.

</timer-normalized>

Thanks for stopping by.

This a project from rubén rodríguez, with thoughtful input from Micah Barrett & Jay Marol.