Carousels Don’t Have to be Complicated

Every time a carousel is mentioned in a blog post, it’s a requirement to mention that… say it with me now:

You probably shouldn’t use a carousel.

Jared Smith made a microsite just for it, with research and rhetoric supporting the idea that you shouldn’t use them. Most of that information focuses on the fact that there is low engagement with the non-first-slide(s).

I’m not here to argue with data, but I would argue with the dogmatism of “never user them, EVER.” Swiping on mobile is an extremely common action, and what is a swipe that reveals more content? Basically a carousel. What about a carousel that doesn’t demand much interaction? Perhaps just a way to swipe through an artist’s recent work. This seems like a perfectly nice way to do that, so long as the UI is clear and accessibility is implemented.

What I am here to talk about is the situation where you do want a carousel and to resist the  temptation to reach for a wheelbarrow full of code to do so. I guarantee there are people who’ve picked an entire CMS because they thought they needed it to make a carousel. No shame. We’re all learning.

I have good news: Carousels don’t have to be complicated. They don’t have to require a ton of code or do anything that you can’t wrap your head around with basic HTML, CSS, and JavaScript knowledge.

A Super Simple Swipeable Slider

What else is a slider but a few panels to swipe or scroll through? Let’s make one with five slides. A parent div with five divs inside:

<div class="slider">
  <div>Slide #1</div>
  <div>Slide #2</div>
  <div>Slide #3</div>
  <div>Slide #4</div>
  <div>Slide #5</div>

Now we line up them up and make it work:

.slider {
  /* line them up horizontally */
  display: flex;

  /* allow for scrolling */
  overflow-x: auto;

  /* make it smooth on iOS */
  -webkit-overflow-scrolling: touch;
.slider > div {
  /* make sure the width is honored */
  flex-shrink: 0;
  width: 300px;
  height: 300px;

That’s it.


You can even add a really nice touch by adding scrolling snap points:

.slider {
  /* ... */

  /* each slide snaps into place */
  scroll-snap-points-x: repeat(300px);
  scroll-snap-type: mandatory;


Easy, right?! You don’t have to download a library just for that, let alone choose a CMS. Let’s explore some other people’s similar ideas.

Christian Heilmann’s “Keeping it simple: coding a carousel” adds basic controls with a little Vanilla JavaScript

His article has the same concept:

“In this article, I want to approach the creation of a carousel differently: by keeping it as simple as possible while not breaking backwards compatibility or have any dependencies.”

The HTML is similar. In this case, it is an ordered list of panels. Plus, some simple arrow buttons:

<div class="carouselbox">
  <div class="buttons">
    <button class="prev"><span class="offscreen">Previous</span>
    <button class="next">
      <span class="offscreen">Next</span></button>
  <ol class="content">

Christian wires up clicks on the buttons to add (and remove) a .current class to the appropriate panel. If the only JavaScript you know is how to add and remove classes on clicks/taps, that gives you a lot of design control. He adds a bit of a CSS transform and it results in this:


Remy Sharp’s “Coda Slider Effect”

I remember watching this for the first time and being absolutely captivated. These were the days when I was just learning JavaScript for the first time, by way of jQuery. Created in 2008, this video tutorial might feel a little dated. The point of this article is not reaching for libraries, right? Yes, but really, the spirit of this article is being empowered to write stuff like this yourself, and that’s exactly what Remy walks you through here.

I would emphasize just how the progressive enhancement part works. The navigation for the slider uses anchor links to jump the slider to the correct position. Check out the markup:

<div class="slider">

  <ul class="nav">
    <li><a href="#slide-1">Slide 1</a></li>
    <li><a href="#slide-2">Slide 2</a></li>
    <li><a href="#slide-3">Slide 3</a></li>

  <div class="mover">
    <div class="panel" id="slide-1"></div>
    <div class="panel" id="slide-2"></div>
    <div class="panel" id="slide-3"></div>


See how the links are anchors to the corresponding panels? If you design the slider such that the panels are in a horizontally scrolling area, the jump links will still work, scrolling that area so that the panel is in view.

The rest of Remy’s tutorial is all about enhancing that basic functionality by adding controls, smooth scrolling and more bells and whistles.


Remy did another carousel tutorial as well that’s even more clever, incorporating the idea of infinite (repeated) scrolling.

What about auto-play? See Jonathan Snook’s “Simplest jQuery Slideshow”

Heading back to the ultrasimple, Jonathan just put some images into a div:

<div class="slideshow">
  <img src="image-1.jpg">
  <img src="image-2.jpg">
  <img src="image-3.jpg">

And, with a handful of lines of jQuery, makes it an auto-playing, cross-fading slideshow:

$('.fadein img:gt(0)').hide();
  $('.fadein :first-child').fadeOut()

Easy cheesy:


I did a very similar thing, which faded in-and-out div’s instead of images directly. Great minds think alike.

Auto-play can be done in CSS as well

Check out Una Kravets’ demo, which uses @keyframes to change the translateX value of the slider for the movement.

#slide-holder {
  /* wide enough to fit all the slides */
  width: 400%;
  position: relative;
  animation: scroller 10s infinite;

/* need a step for each slide */
@keyframes scroller {
  0%   { transform: translateX(0); }
  33%  { transform: translateX(-400px); }
  66%  { transform: translateX(-800px); }
  100% { transform: translateX(0); }

I did the same exact thing, only use an image that repeated itself a little so that the carousel could be infinite.

There is plenty of trickery going on in Gallery CSS, but it’s just HTML and CSS. No dependencies. It works by utilizing the :target selector in CSS. You click an anchor link, the element with the matching ID is now “targeted”, and through clever CSS selectors involving the ~ general sibling combinator, you can hide/show the appropriate panel.


You can just snag Ben’s code to use (it’s pretty small), but it’s more useful to understand the concept at work.

<div class="gallery items-3">
  <div id="item-1" class="control-operator"></div>
  <div id="item-2" class="control-operator"></div>

  <figure class="item">

  <figure class="item">

  <div class="controls">
    <a href="#item-1" class="control-button"></a>
    <a href="#item-2" class="control-button"></a>

All the panels (.item) are hidden (except the first/default one). e.g.

.item:not(:first-of-type) {
  opacity: 0;
  visibility: hidden;

Say you click on the second link, it becomes the target, and you reveal the panel like:

.control-operator:nth-of-type(2):target ~ .item:nth-of-type(2) {
  opacity: 1;
  visibility: visible;

Opacity changing is but one possibility. You could transform them, use movement, whatever you like.

If you’re going to use a library, Dave DeSandro’s Flickity is a great choice

If you decide to go with a library, I think the time to do that is when you want something that the library makes particularly awesome and also makes it particularly easy.

I think Flickity does that very well. You don’t need to write any JavaScript at all to make it work, you just put an attribute on some HTML, like:

<div class="carousel" data-flickity>
  <div class="carousel-cell"></div>
  <div class="carousel-cell"></div>


But you can also configure it with loads of useful options, and it includes a robust API. And of course, like a good slider should be: responsive and touch-friendly.


  • A. M. Douglas

    There’s also the Web Animation API (WAAPI), which gives developers programmatic control over CSS3 Animations.

    Which might not seem all that useful, but the CSS-only method more or less requires that either:
    – the widths are known in advance
    – the carousel elements are full-width
    – the number of elements is known in advance and the elements are all the same width

    With the latter two cases, you can use percentages in your transforms to get a smooth carousel going without overshooting it.

    With the first case, fixed-width images aren’t the worst thing in the world, but it’s not the best solution for responsive web design.

    JavaScript gives us the power to handle any number of elements of differing widths.

    Using the new Web Animation API, an infinitely scrolling, direction-inverting, marquee-style carousel might look like this:

    The best part: it gracefully degrades to a grid of items. In an ideal world, the elements in your grid would be equal width, or you’d be using flexbox, so no harm would be done.

    The second best part: you can pause and play the animation, e.g. when a user hovers, perhaps…

    Will be very cool for displaying a selection of clients’ logos, perhaps. Maybe featured products, albums etc.

  • Super Powered

    I feel obligated to shout out my personal favorite slider library, slick.
    Super easy to use and configure, and 100% free.

    • A. M. Douglas

      For a more bare-bones vanilla JavaScript slider, there’s also lory.

      • Winston

        Lory caused me so many problems. It was very buggy. I ended up switching to Flickity which is also vanilla JS and it was so much better.

    • Mateusz Czpak

      When comes to use slider library I agree, Slick slider is really good. Option “responsive” is great, because you can easily change slider settings on custom breakpoints.

  • Sarah Rath

    do these swipeable sliders only work on mobile? because I tested the first one out on desktop and can’t get it to slide unless i drag the scroll bar.

  • Scrapper

    I’m totally on board with this and the general idea of not over-complicating things and reducing bloat, but while this may be something to work towards I don’t think its representative of real world requirements right now – a world where we have to support more than just the latest versions of modern browsers (or for example, the latest version of Chrome which does not support CSS snap points)

  • Yngve Bakken-Nilsen

    Hmm… scroll-snap isn’t (and wont be, it seems) supported on any chrome versions…

  • Super Powered Slick has a “Variable Width” option, which works pretty well, but will create cutoff. Otherwise the other option is to have a fixed width and use “overflow: hidden;” or something to that extent to hide any slides from the edges.

  • Chris,
    We couldn’t agree more with this, sometimes a carousel can actually hurt conversions depending on the bussiness model, but a nice looking image can really set the tone for a site. Has a nice big simple image slider on the homepage and yes we track the button clicks 😉 we also like to hide sliders on mobile devices to help load times.

  • Marc

    I’d like to introduce you to my own “Simple Slider” at – the slides in this Examle are filled with an (background) image, text and a button for further reading. It has controls for pause/unpause/start from beginning and jumping to any of the slides by using the checkbox-hack. So no JS required.

    Also it has some (very strong 😉 ) highlighting for focus and is keyboard accessible – anyway it is designed as inclusive design – if you find some bugs (especially accordign to accessibility): PLEASE REPORT!

    It has no design. So go ahead, make your – it is free to use for private and/or commercial sites. Use it, fork it, make it yours! Just mention me, that ‘d be great! And I’d love to see, what you use it for…

  • xem

    Here’s another touchable slider I made a few years ago, featuring an API and the ability to make some elements cancel the swipe in your slides:

  • Elsa Gong

    So, this is a marketing article for Flickity? Oh no, I almost fell in love with your blogs😂😂

  • Leanne Banks

    This is one of the best blogs! Gonna try to make my own carousel based on this.

Related Articles