Making a carousel component without JavaScript

Photo by Henry Be

At carwow we’ve recently worked on updating some of our UI components with the aim of getting rid of any third-party JavaScript library, or even, any JS at all. We replaced our old and heavy carousel plugin with a simple, responsive and efficient component using only HTML and CSS.

If you just want to see the demo and code, you can jump straight to the CodePen. 💅

The specifications

Our goal was to have a flexible component which can embed pretty much any HTML, as well as give the possibility to display multiple items on each slide. The number of items per slide would be flexible per usage as well as per responsive breakpoint.

On small and medium screens, the carousel should show the list of items without any controls, as the user would swipe horizontally to go through them. All we need is to show a small part of the next slide on the right side as a cue to swipe left. Here is an example from our new and pretty homepage:

Small / Medium screens

On large screens, control arrows are shown on the sides and the user can click them to go through the slides one by one.

Large screen

Benefits and limitations

Before we dig into the code, I would like to share some thoughts on this approach. In the past we used a few dedicated jQuery plugins to build carousels. These solutions are very quick to implement, but come with problems: heavy resources to load, most functionalities they bring won’t be used in your application and some of their behaviours and styles can be difficult to override.

Instead, we can have a very lightweight and simple tool that fits exactly our need. However, because our component functions based on a static language like CSS, we are limited to set a maximum number of slides, which we set to 10. I actually think this limitation is a good thing as it sets a constraint that shouldn’t be exceeded anyway. If you have a carousel with more than 10 slides on your website and expect users to navigate through them, maybe you’re using the wrong component and should re-consider your user experience.

The markup

The carousel’s markup is composed of 3 parts:

  • The activators: a number of hidden radio buttons that control which slide is shown on large screens. Activators need to be at the top of the markup as we will use the general sibling combinator in CSS ~ to move the slider based on which activator is selected.
  • The control arrows: because we don’t use JavaScript, we will need to duplicate the control arrows for each slide. These are labels pointing to the activators of the previous/next slide.
  • The track: the container of all the slides. We’re calling it a track as the slides are horizontally aligned, with an hidden overflow. We will be using the transform CSS property to move the track to the right position based on the current checked activator.

To function, the carousel needs as many activators and control arrows as the number of slides on large screens.

For our demo, we will display 7 items in the carousel, using the same layout as described by the mockup above: 1 item per slide on small screens, 2 on medium screens and 3 on large screens. We use a separate helper class for each specification on every item of the carousel: carousel__item — mobile-in-1, carousel__item — tablet-in-2, carousel__item — desktop-in-3

No JS, no problems

In our example, we start with the styles for large screens, although the same result can be achieved using a mobile-first approach.

To have the carousel working, the key is to:

  • Hide .carousel__controls using display: none;
  • Get .carousel__item elements to display in a single line using display: inline-flex; and adding white-space: nowrap on the .carousel__track
  • Hide anything beyond the carousel by setting overflow: hidden;on .carousel__screen
  • Using a for loop in your favourite preprocessor, the nth-of-type selector and the ~ combinator, display the .carousel__controls based on the position of the currently checked .carousel__activator
  • Using the same loop as above, set a translation of the .carousel__trackusing transform: translateX();

Swipe left or right

On medium and small screens, we choose to hide the controls and let the user swipe through the carousel items. To do this, we simply need to undo some of the styles previously defined. Then we simply need to:

  • Set .carousel__track to display using overflow-x: auto;
  • Define the size of the carousel elements, using help classes generated by another for loop. We use a reference of 90% of the parent element’s width in order to have the next element visible on the right side of the screen.

The demo

Check out the CodePen below or the component on our lovely design system.

If you have any questions or thoughts on our approach, we’d love to hear them!