Skip to main content

/ Treasa Ní Chonchúir

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

  1. Usage
  2. Example
  3. How it works

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:

A large room with many columns. Between the columns are many red and white double arches in the Moorish style. In the distance you can see that alterations have been made to the building; in between some of the distant arches is a gray structure with Gothic elements.
Moorish archways as far as the eye can see. I tried several times to capture the way this feels infinite, but the photos don’t do it justice.
A large room with many columns. Between the columns are many red and white double arches in the Moorish style. It’s impossible to tell how far they extend; from this angle, it seems like the arches extend indefinitely.
The hypostyle prayer hall.
An ornate medieval chapel. At first you would asssume this is an entirely different building than the first two photos, but then you see the basic design of the double arches extends into here. This European chapel has been inserted rather directly into the enclosing Moorish building.
The Capilla Sagrario
The arches and wall of the mihrab are more ornate than in the hypostyle.
The mihrab.
Detail of some of the mihrab arches.
Another view of the hypostyle; the rows and rows of arches and columns extends far into the distance.
You can see the renaissance ceiling of the main cathedral juxtaposed with the Moorish double arches of the hypostyle just beyond it.
An especially ornate Moorish arch.

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.