The <tess-carousel>
element
When I imported all my social media posts I brought over many that include multiple images in them. From Instagram mainly, but lots of tweets have multiple images too. I decided I really ought to make a nice, IG-style, scrolly carousel thingy for them.
This is a pretty good use case for custom elements. Of course I’ll want the carousel to work just fine without JavaScript as well. The markup should be as simple as possible, within the bounds of sensible progressive enhancement.
Table of Contents
Usage
This is what the markup for a <tess-carousel>
typically looks like:
<tess-carousel controls>
<figure>
<img src=/2023/09/mezquita/IMG_4462.jpg
alt="Imagine I wrote an elaborate description of the Moorish arch
that appears in this photo.">
<figcaption>An especially ornate Moorish arch.</figcaption>
</figure>
<!-- … more <figure>s … -->
</tess-carousel>
As you can see, the <tess-carousel>
element simply
contains the things you want to be in the carousel. In this example
it has <figure>
element children, but anything
should work. For importing posts from Instagram—where a group of images
typically has one, shared caption—you’d do it the other way around:
<figure>
<tess-carousel controls>
<img src=/2023/09/mezquita/IMG_4462.jpg
alt="An especially ornate Moorish arch.">
<!-- … more <img>s … -->
</tess-carousel>
<figcaption>A caption for all the images, that really ties the room
together. #latergram</figcaption>
</figure>
The presence or absence of a controls
attribute
determines if there are visible buttons for scrolling the carousel, much
like <video controls>
in HTML does.
Example
Here’s an example of a <tess-carousel>
featuring
several photos I took at the Mosque–Cathedral of Córdoba:








How it works
To use a <tess-carousel>
element on a page, I load a
stylesheet and a JavaScript file, and just use the element as if it were
bog-standard HTML.
The stylesheet makes the carousel work even if JavaScript is disabled by using a combination of scroll snapping and flexbox:
tess-carousel {
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
}
tess-carousel > * {
scroll-snap-align: start;
flex-shrink: 0;
width: 100%;
}
(There’s a bit more to it than this, but you get the gist.)
This is a reasonable place to stop. The carousel works, and doesn’t require JavaScript at all.
But I also wanted to have on-screen controls for
paging through the carousel, in addition to scrolling. For this we’ll
need some JavaScript. It defines a
Carousel
subclass of HTMLElement
and wires it
up to the tess-carousel
element name.
I use
Shadow DOM to wrap the carousel items (the Light DOM children of the
<tess-carousel>
element) into a container to which I apply
the scroll snap & flexbox styles to. This makes it way easier to
position the controls within the carousel element.
One gotcha with this approach is that the stylesheet above doesn’t
work for this Shadow DOM structure, so the javascript removes the
<link>
element that points at the stylesheet when it
loads.