Andi Smith

CSS3 Flexbox Carousel

Carousels are one of the most common components web developer's build. As other trends come and go, carousels tend to stay.

Recently I've been using the nth-child CSS pseudo selector when building CSS3 demos for my "Introduction to CSS3″ series on The Daily Egg; to the point where nth- child became a bit of a habit. For those of you who have not come across nth-child, it allows you to target a particular child elemehnt by number or a simple formula.

So I was building a carousel the other week and I used nth-child to create my dummy carousel items. Each item had a different background color.

It was all working splendidly until I needed to make the carousel loop. Then as it looped, my nth-child changed. As you are probably aware, the problem with looping carousels is that you have to repeat your items so something can animate in from the left or right.

Looping carousel

Repeating items in a carousel has always bothered me. It feels like a messy solution, but it gives the right visual appearance and works cross browser.

Then I began thinking about a presentation I gave recently on "Layouts of the Future", in which I spoke about current hacks we have to make to create CSS layouts and how help is round the corner with the likes of columns; CSS Regions and… flexbox.

Flexbox allows us to build layouts horizontal or vertical layouts with relative ease and flexes the contained boxes to fill the area available. But, one of the other cool things about flexbox is that you can rearrange your layout by using the box-ordinal-group property.

For example, here are three boxes, layed out with Flexbox:

Flex layout 1

To change the order of these boxes, we simply change the box-ordinal-group property on each item. For item 2, I've set the box-ordinal-group property to 1 so that it is first; item 1 is now set to 2, and the third item remains 3.

#item2 {  
  -moz-box-ordinal-group: 1;
  -ms-box-ordinal-group: 1;
  -webkit-box-ordinal-group: 1; 
  -o-box-ordinal-group: 1;
  box-ordinal-group: 1;
}
Flex layout 2

This is pretty cool as we no longer have to worry about markup order or floats. And thus my trail of thought moved on to flexbox for carousels.

If you don't wish to read more about how the carousel was made, you can play with it here.

Implementation

With a carousel, you probably wouldn't want to flex your items to fill the space (which is ok, as flexing has to be turned on) but we are concerned with the order of the items.

Building a carousel with flexbox is relatively straightforward once you get past the vendor prefixes:

#carousel {
  overflow:hidden;
  position:relative;
  width:auto;
}

#carousel ul { 
  -moz-box-orient:horizontal;
  -ms-box-orient:horizontal;
  -webkit-box-orient:horizontal;
  -o-box-orient:horizontal;
  box-orient:horizontal;

  display:-moz-box;
  display:-ms-box;
  display:-webkit-box;
  display:-o-box;
  display:box;

  list-style-type:none;
  margin:0;
  padding:0;
}

#carousel li {
  height:200px;
  margin-right:10px;
  width:200px;
}

Here, we are telling our un-ordered list that we wish to display as a flexbox layout on a horizontal plane. (As an aside, I've started placing Opera's vendor prefixes below Webkit's so that if Opera supports the Webkit property, it cascades to receive the O version. Not that Opera supports flexbox at the moment.)

If we maintain an active counter on our carousel, we can change the box- ordinal-group whenever the user clicks next or previous and never have to worry about repeating items again.

The functionality in our JavaScript for changing the ordinal is relatively straightforward, but to ensure we have an element on the left to animate to we need to start our ordinal at the active value-1, and then add a left margin to our carousel that is one item wide.

function changeOrdinal() {

  var length = items.children.length, 
      ordinal = 0;

  // start at the item BEFORE the active one.
  var index = active-1;

  /* if the active item was 0, we're now at -1 so
      set to the last item */
  if (index < 0) {
      index = length-1;
  }

  // now run through adding the ordinals
  while ( ordinal < length ) {
      // add 1 to the ordinal - ordinal cannot be 0.
      ordinal++;

      // check the item definetely exists :)
      var item = items.children[index];
      if ( item && item.style ) {
          // new ordinal value
          item.style[boxOrdinalGroup] = ordinal;
      }

      /* as we are working from active we need to go back to
         the start if we reach the end of the item list */
      if ( index < length-1 ) {
          index++;
      } else {
          index = 0;
      }
  }
}

I've commented the code on Github if you wish to dive deeper in to how it works.

Summary

The final flexbox carousel is available to play with on Github.

Using flexbox comes at a browser support cost, however. The specification was recently changed, which means most browsers only partially support parts of Flexbox - but the parts used in this demo work in Chrome, Firefox and Safari. Flexbox also doesn't work in Opera, or IE9 or below. But perhaps this is a solution we can use in the future once everything has settled down?

Here's a link to the demo and the code is available on Github. Enjoy!