How to Create a Simple Multi-Item Slider

A tutorial on how to create a simple category slider with a minimal design using CSS animations and jQuery. The idea is to slide the items sequentially depending on the slide direction.

How to Create a Simple Multi-Item Slider

For today’s tutorial we want to show you how to create a simple item slider with CSS animations and some jQuery. The idea was inspired by the Aplle product slider where several little items fly in with a bouncing animation. We wanted to translate this concept to a modern-looking alternative for a minimal online store design where the items represent different categories. Categories as use-case fit quite well because of the limited nature of this type of slider. For more items to be shown, this is certainly a far-from-optimal solution. If the amount of items is limited this might give an interesting little touch to the experience.

So, let’s get started with this last tutorial of 2012!

The Markup

For the HTML we will use a division that wraps some unordered lists that will contain the items and a navigation with the category links. Each list item will have a link that contains an image and a h4 heading.

<div id="mi-slider" class="mi-slider">
		<li><a href="#"><img src="images/1.jpg" alt="img01"><h4>Boots</h4></a></li>
		<li><a href="#"><img src="images/2.jpg" alt="img02"><h4>Oxfords</h4></a></li>
		<li><a href="#"><img src="images/3.jpg" alt="img03"><h4>Loafers</h4></a></li>
		<li><a href="#"><img src="images/4.jpg" alt="img04"><h4>Sneakers</h4></a></li>
		<li><a href="#"><img src="images/5.jpg" alt="img05"><h4>Belts</h4></a></li>
		<li><a href="#"><img src="images/6.jpg" alt="img06"><h4>Hats & Caps</h4></a></li>
		<li><a href="#"><img src="images/7.jpg" alt="img07"><h4>Sunglasses</h4></a></li>
		<li><a href="#"><img src="images/8.jpg" alt="img08"><h4>Scarves</h4></a></li>
		<li><a href="#"><img src="images/9.jpg" alt="img09"><h4>Casual</h4></a></li>
		<li><a href="#"><img src="images/10.jpg" alt="img10"><h4>Luxury</h4></a></li>
		<li><a href="#"><img src="images/11.jpg" alt="img11"><h4>Sport</h4></a></li>
		<li><a href="#"><img src="images/12.jpg" alt="img12"><h4>Carry-Ons</h4></a></li>
		<li><a href="#"><img src="images/13.jpg" alt="img13"><h4>Duffel Bags</h4></a></li>
		<li><a href="#"><img src="images/14.jpg" alt="img14"><h4>Laptop Bags</h4></a></li>
		<li><a href="#"><img src="images/15.jpg" alt="img15"><h4>Briefcases</h4></a></li>
		<a href="#">Shoes</a>
		<a href="#">Accessories</a>
		<a href="#">Watches</a>
		<a href="#">Bags</a>

Let’s take a look at the style.


Note that the CSS will not contain any vendor prefixes, but you will find them in the files.
What we want to do is the following: initially we just want the first item list to show while all the other lis will be translated to the right, outside of the viewport. When we click on a navigation link the items will either slide in from the right or from the left, depending on which current position we are at and what the newly selected category is.

Let’s first style the wrapper, which is the division with the class mi-slider. It will have a predefined height which we need in order to set the position of the uls correctly:

.mi-slider {
	position: relative;
	margin-top: 30px;
	height: 490px;

The ul will be positioned absolutely which means that all the lists will be on top of each other. Remember, we only want to translate the list items, not the lists themselves. We set the pointer-events to none because we want to be able to click on the current list’s links:

.mi-slider ul {
	list-style-type: none;
	position: absolute;
	width: 100%;
	left: 0;
	bottom: 140px;
	overflow: hidden;
	text-align: center;
	pointer-events: none;

The pointer events of the current list should be reset, so that the containing links are clickable:

.mi-slider ul.mi-current {
	pointer-events: auto;

When JavaScript is disabled, we don’t want anything to look broken (we use Modernizr):

.no-js .mi-slider ul {
	position: relative;
	left: auto;
	bottom: auto;
	margin: 0;
	overflow: visible;

In order to center all the list items, we have given the ul a text-align of center and now we’ll give the list items an inline-block display with a width of 20%. This width makes sure that our items fit into the list and keeps it fluid.
By default, all the list items will be translated to the right. We use 600% here because it’s a large enought value to move them out of the viewport. We’ll also add a little transition for the opacity:

.mi-slider ul li {
	display: inline-block;
	padding: 20px;
	width: 20%;
	max-width: 300px;
	transform: translateX(600%);
	transition: opacity 0.2s linear;

Whithout JS we don’t want them to translate:

.no-js .mi-slider ul li {
	transform: translateX(0);

Let’s style the content of the list items. Note how we set the max-width of the images to 100%. This will ensure that the layout does not break but the images will resize according to their wrapper which is our li with a percentage-based width:

.mi-slider ul li a,
.mi-slider ul li img {
	display: block;
	margin: 0 auto;

.mi-slider ul li a {
	outline: none;
	cursor: pointer;

.mi-slider ul li img {
	max-width: 100%;
	border: none;

.mi-slider ul li h4 {
	display: inline-block;
	font-family: Baskerville, "Baskerville Old Face", "Hoefler Text", Garamond, "Times New Roman", serif;
	font-style: italic;
	font-weight: 400;
	font-size: 18px;
	padding: 20px 10px 0;

On hover, we’ll animate the opacity of the list item:

.mi-slider ul li:hover {
	opacity: 0.7;

The navigation needs to have a top value because the lists are positioned absolutely. We’ll center the navigation by giving lateral auto margins and we’ll set a maximum width of 800px:

.mi-slider nav {
	position: relative;
	top: 400px;
	text-align: center;
	max-width: 800px;
	margin: 0 auto;
	border-top: 5px solid #333;

We don’t want to show the navigation at all when there is no JavaScript enabled:

.no-js nav {
	display: none;

The navigation links will have a generous padding and we’ll give them a transition for hover:

.mi-slider nav a {
	display: inline-block;
	text-transform: uppercase;
	letter-spacing: 5px;
	padding: 40px 30px 30px 34px;
	position: relative;
	color: #888;
	outline: none;
	transition: color 0.2s linear;

.mi-slider nav a:hover,
.mi-slider nav a.mi-selected {
	color: #000;

The mi-selected class, just like the mi-current class for the lists, we’ll set using JavaScript.

Now, let’s add that little arrow on the top. We’ll use the pseudo-classes :before and :after to create two triangles with borders:

.mi-slider nav a.mi-selected:after,
.mi-slider nav a.mi-selected:before {
	content: '';
	position: absolute;
	top: -5px;
	border: solid transparent;
	height: 0;
	width: 0;
	position: absolute;
	pointer-events: none;

.mi-slider nav a.mi-selected:after {
	border-color: transparent;
	border-top-color: #fff;
	border-width: 20px;
	left: 50%;
	margin-left: -20px;

.mi-slider nav a.mi-selected:before {
	border-color: transparent;
	border-top-color: #333;
	border-width: 27px;
	left: 50%;
	margin-left: -27px;

Let get to the juicy bits, the animations. The first animation that we want to happen, is the scaling up of the items of the first list. The scaleUp animation will also include the items being translated to 0 because we want them to be in the viewport:

.mi-slider ul:first-child li,
.no-js .mi-slider ul li {
	animation: scaleUp 350ms ease-in-out both;

@keyframes scaleUp {
	0% { transform: translateX(0) scale(0); }
	100% { transform: translateX(0) scale(1); }

Let’s add a different delay to each list item so that they appear sequentially:

.mi-slider ul:first-child li:first-child {
	animation-delay: 90ms;

.mi-slider ul:first-child li:nth-child(2) {
	animation-delay: 180ms;

.mi-slider ul:first-child li:nth-child(3) {
	animation-delay: 270ms;

.mi-slider ul:first-child li:nth-child(4) {
	animation-delay: 360ms;

For our example we will just have a maximum of four items, so we’ll only define four delays. If we would have more items, we would include more delays.

For the sliding animations we will have four cases: two for the sliding in of new items and two for the sliding out of the current items, depending on the direction. So we’ll define four classes for the lists which we will add with JavaScript:

/* moveFromRight */

.mi-slider ul.mi-moveFromRight li {
	animation: moveFromRight 350ms ease-in-out both;

/* moveFromLeft */

.mi-slider ul.mi-moveFromLeft li {
	animation: moveFromLeft 350ms ease-in-out both;

/* moveToRight */

.mi-slider ul.mi-moveToRight li {
	animation: moveToRight 350ms ease-in-out both;

/* moveToLeft */

.mi-slider ul.mi-moveToLeft li {
	animation: moveToLeft 350ms ease-in-out both;

Now we need to set the according animation delays which will also depend on the direction. For example, the first item will slide in with no delay if it’s coming form the right side and also when it slides out to the left. The same will be true for the last item:

.mi-slider ul.mi-moveToLeft li:first-child,
.mi-slider ul.mi-moveFromRight li:first-child,
.mi-slider ul.mi-moveToRight li:nth-child(4),
.mi-slider ul.mi-moveFromLeft li:nth-child(4) {
	animation-delay: 0ms;

The increased delays will be set accordingly:

.mi-slider ul.mi-moveToLeft li:nth-child(2),
.mi-slider ul.mi-moveFromRight li:nth-child(2),
.mi-slider ul.mi-moveToRight li:nth-child(3),
.mi-slider ul.mi-moveFromLeft li:nth-child(3) {
	-webkit-animation-delay: 90ms;
	animation-delay: 90ms;

.mi-slider ul.mi-moveToLeft li:nth-child(3),
.mi-slider ul.mi-moveFromRight li:nth-child(3),
.mi-slider ul.mi-moveToRight li:nth-child(2),
.mi-slider ul.mi-moveFromLeft li:nth-child(2) {
	-webkit-animation-delay: 180ms;
	animation-delay: 180ms;

.mi-slider ul.mi-moveToLeft li:nth-child(4),
.mi-slider ul.mi-moveFromRight li:nth-child(4),
.mi-slider ul.mi-moveToRight li:first-child,
.mi-slider ul.mi-moveFromLeft li:first-child  {
	-webkit-animation-delay: 270ms;
	animation-delay: 270ms;

And now let’s define the animations itself. For example, moving from the right will mean that we will set the translateX value to 600% and move to 0. Moving from the left, we’ll set the initial position to -600% so that the items are on the left side outside of the viewport. And so on:

@keyframes moveFromRight {
	0% { transform: translateX(600%); }
	100% { transform: translateX(0); }

@keyframes moveFromLeft {
	0% { transform: translateX(-600%); }
	100% { transform: translateX(0); }

@keyframes moveToRight {
	0% { transform: translateX(0%); }
	100% { transform: translateX(600%); }

@keyframes moveToLeft {
	0% { transform: translateX(0%); }
	100% { transform: translateX(-600%); }

Last, but not least, let’s use some media queries to adjust the slider content for smaller screens.

We’ll start with adjusting the navigation so that it does not break when the screen is too small:

@media screen and (max-width: 910px){
	.mi-slider nav {
		max-width: 90%;

	.mi-slider nav a {
		font-size: 12px;
		padding: 40px 10px 30px 14px;

Since we set a fixed height to the slider, we want to make sure it adapts:

@media screen and (max-width: 740px){
	.mi-slider {
		height: 300px;

	.mi-slider nav {
		top: 220px;

For really small screens, we don’t simply want to make everything super tiny, but instead we want to make the navigation easy for touch devices. So we’ll simply show all the categories. We’ll set all the styles in a way that nothing is hidden and all the lists are displayed under each other:

@media screen and (max-width: 490px){ 
	.mi-slider {
		text-align: center;
		height: auto;

	.mi-slider ul {
		position: relative;
		display: inline;
		bottom: auto;
		pointer-events: auto;

	.mi-slider ul li {
		animation: none !important;
		transform: translateX(0) !important;
		padding: 10px 3px;
		min-width: 140px;

	.mi-slider nav {
		display: none;

And that’s all the style. Now let’s control some things with jQuery.

The JavaScript

Let’s create a simple jQuery plugin for our slider. Most of the work is done in the CSS where we have defined all the animations. The plugin will mainly focus on adding and removing classes and control the current category being shown. For browsers that don’t support animations we will fall back to a simple show/hide approach.

Let’s start by caching some elements and initialize some variables:

_init : function( options ) {

	// the categories (ul)
	this.$categories = this.$el.children( 'ul' );
	// the navigation
	this.$navcategories = this.$el.find( 'nav > a' );
	var animEndEventNames = {
		'WebkitAnimation' : 'webkitAnimationEnd',
		'OAnimation' : 'oAnimationEnd',
		'msAnimation' : 'MSAnimationEnd',
		'animation' : 'animationend'
	// animation end event name
	this.animEndEventName = animEndEventNames[ Modernizr.prefixed( 'animation' ) ];
	// animations and transforms support = Modernizr.csstransforms && Modernizr.cssanimations;
	// if currently animating
	this.isAnimating = false;
	// current category
	this.current = 0;
	var $currcat = this.$categories.eq( 0 );
	if( ! ) {
	else {
		$currcat.addClass( 'mi-current' );
	// current nav category
	this.$navcategories.eq( 0 ).addClass( 'mi-selected' );
	// initialize the events


We will bind the click event to the navigation category links under the slider. We assume that the index of each one corresponds to the index of the respective category (the ul). When a category link is clicked the items of the current category fly out, and the ones of the new category fly in one by one (remember, we defined the animation delays in the CSS).

_initEvents : function() {

	var self = this;
	this.$navcategories.on( 'click.catslider', function() {
		self.showCategory( $( this ).index() );
		return false;
	} );

	// reset on window resize..
	$( window ).on( 'resize', function() {
		self.$categories.removeClass().eq( 0 ).addClass( 'mi-current' );
		self.$navcategories.eq( self.current ).removeClass( 'mi-selected' ).end().eq( 0 ).addClass( 'mi-selected' );
		self.current = 0;
	} );


showCategory : function( catidx ) {

	if( catidx === this.current || this.isAnimating ) {
		return false;
	this.isAnimating = true;
	// update selected navigation
	this.$navcategories.eq( this.current ).removeClass( 'mi-selected' ).end().eq( catidx ).addClass( 'mi-selected' );

	var dir = catidx > this.current ? 'right' : 'left',
		toClass = dir === 'right' ? 'mi-moveToLeft' : 'mi-moveToRight',
		fromClass = dir === 'right' ? 'mi-moveFromRight' : 'mi-moveFromLeft',
		// current category
		$currcat = this.$categories.eq( this.current ),
		// new category
		$newcat = this.$categories.eq( catidx ),
		$newcatchild = $newcat.children(),
		lastEnter = dir === 'right' ? $newcatchild.length - 1 : 0,
		self = this;

	if( ) {

		$currcat.removeClass().addClass( toClass );
		setTimeout( function() {

			$newcat.removeClass().addClass( fromClass );
			$newcatchild.eq( lastEnter ).on( self.animEndEventName, function() {

				$( this ).off( self.animEndEventName );
				$newcat.addClass( 'mi-current' );
				self.current = catidx;
				var $this = $( this );
				// solve chrome bug
				self.forceRedraw( $this.get(0) );
				self.isAnimating = false;

			} );

		}, $newcatchild.length * 90 );

	else {

		this.current = catidx;
		this.isAnimating = false;



And that’s it! I hope you enjoyed this tutorial and find it useful and inspiring!
We wish you a happy new year!

Tagged with:

Manoela Ilic

Manoela is the main tinkerer at Codrops. With a background in coding and passion for all things design, she creates web experiments and keeps frontend professionals informed about the latest trends.

Stay in the loop: Get your dose of frontend twice a week

๐Ÿ‘พ Hey! Looking for the latest in frontend? Twice a week, we'll deliver the freshest frontend news, website inspo, cool code demos, videos and UI animations right to your inbox.

Zero fluff, all quality, to make your Mondays and Thursdays more creative!

Feedback 117

Comments are closed.
  1. Very nice Slider. I inserted it on my test site and it works greate. One question: how to make Slider stay in active category after we click on it?

  2. Thank you for these magnificent contribution, very useful and attractive.
    Best wishes.

  3. Hi there,

    Great slider, I love it , but my question is if there’s a way to put the navigation on top of the products.


  4. If anyone can help solving its compatibility issue with firefox 15 ….thaaat would be great! ๐Ÿ˜›

  5. It’s exactly what I’m looking for. I was wondering if anyone has solved the issues of flipping nav/filter bar to the top and having two rows.

    Just wanted to bring those questions up again as I couldn’t find any answers ๐Ÿ™‚

  6. I’ve tried to use it on my website but somehow it’s conflicting with the revolution slider on the same page. The revolution slider won’t load and the multi item slider won’t adjust to the CSS rules.

  7. Hi,

    Will this work on the mobile, I mean is it possible to touch-slide from category to category?


  8. Nice tutorial…..WOW! but how do i make it auto play…seems its only on click before transition.

  9. As I said before, this is a great tutorial. Has anyone used it with WordPress? After trying it, everything has crashed (website, MAMP). I’ve spent two days trying to fix everything. I ended up giving up and restoring an old version from my Time Machine. Does anyone have this problem? I somehow fell in love with this slider and I don’t want to give it up …

  10. I’ve now been trying to make it work for over a week. I’m just desperate.

    • Celine, do you have a test version online so that I can take a look? Cheers, ML

  11. Very nice menu.
    I tried to implement it into Drupal 7.x, creting a page with the required code, and importing the .js files using the .info file which is with the theme.
    But not matter what theme i’m using, in Drupal I got several issues:

    The downward triangle is not aligned with the horizontal line. I realigned it with the CSS
    With 4 items:
    if I click on the first NAV item, the triangale shits to the 2nd. If I click on the second NAV item, the triangle shifts to the 3rd.
    If now I click back on the 1st item, the triangle shifts to the second one.
    if I click on the 4th item, the triangle disappears.
    Once disappeared, if I click on any of the items: it doesn’t appears back anymore

    Any suggestion please?

    • I found a solution. I have not clue how elegant it is but please if you want to solve it quickly and you got the same issue as me:
      Put the tags on the same line. Do not break the line.

      Current code:
      <a href="#" >First item</a> <a href="#" >Second item</a> <a href="#" >Third item</a> <a href="#" >Fourth item</a>

      Porposed soluton (to me it works):

      <a href="#">First item</a><a href="#" >Second item</a><a href="#">Third item</a><a href="#"">Fourth item</a>

      If there is any other solution, I’m really willing to experiment it

  12. Hello Mary Lou and thank you for your message.

    I have started working again on the slider today. I did manage to make it work. However, no matter what I try (adding it directly to the template or via the php include function) it’s always conflicting with my revolution slider. I can’t find how to fix this and for now I believe I’m doomed to chose between your slider or the revolution slider …

  13. Hello, first of thanks for the great tutorial, i love it! However i have a problem with using special charachters in links since it turns them to [?] even though my site is set to UTF8 in a proper way… any ideas? Big thanks, cheers.

  14. Slider fails after the first round, can you point and tell me what the issue is?

  15. I tried adding more objects to the list but the list adjusts itself in a block and it kinda looks messy…any solution abt how to add images which can slide

  16. I’m new to this, it’s a really basic question but

  17. Has anyone managed to resolve the issue with the nav links not working? Appears to happen when you click links left to right, just stops working. If you click at random its fine. Happens in Chrome v29, ie9 & ie10

  18. i have implemented the slider bt it doesnt slide automatically what do you think i should do

  19. Is there a way to set the starting slide instead of having it to start at position zero?

  20. Love this slider.
    I think it could be used for more than 4 products…
    If someone could help add more than 4 lines.. I can’t get my head around the “delays” to adjust to do this..

  21. Setting the slider to utilize the NAV selector makes it incompatible with basically any other project that uses the NAV selector for…you guessed it….navigation. I’ll try to make this work. Thanks.

    • Hi KJ, are you planning to add another nav element into the slider? If you do, just give them classes and replace it in the CSS. Hope it helps, ML

  22. I really love this slider.
    But this is not working on FIrefox 14.0.1 in Ubuntu 10.0.4
    Please help!

  23. Just thought I’d let people know, as I spent a whole afternoon trying to debug, that it appears the slider script doesn’t agree with jquery UI, at least the version currently on the google api.
    I was witnessing a situation where the slider would load fine, would also cycle through one round of categories fine, but when you revisited a nav category you had already clicked, the items would not re-appear and the nav bar would no longer work.
    This was the case in chrome and firefox, and I tried editing the changing the nav element to a div, then editing the css to reflect that, and the js file. That didn’t help, so I set up DOM breakpoints on the first ul, and saw that something weird was going on between lines 82 and 92 and the jquery UI add class functions.

    Anyway got it sorted. Hope that helps someone.

    • Hello,

      I dont suppose you are able to provide source / code to what you changed? I still cant seem to get it working when you re-visit a nav link on any browser

  24. hi Mary Lou,

    I was wondering about the part of the initialization of the plugin. How would you pass the options through? I don’t really understand the reasoning and how it works:

    $.fn.catslider = function( options ) {
    var instance = $.data( this, ‘catslider’ );

    if ( typeof options === ‘string’ ) {
    var args = arguments, 1 );

    this.each(function() {
    instance[ options ].apply( instance, args );
    else {

    this.each(function() {
    instance ? instance._init() : instance = $.data( this, ‘catslider’, new $.CatSlider( options, this ) );
    return instance;

  25. thanks for sharing your knowledge ML; your code works very well on Chrome, but doesn’t work well on Mozilla and IE. What could be the problem here?

  26. Nice Work! Is there anyway to have the products fade in and out instead of sliding? If so, how can I achieve this? Thank you!

  27. Hi ML ! I encounter an issue when inserting images through Magento. The JS loads OK while not entering the WYSIWIG format but there is no picture…

    When I insert the Magento WYSIWIG thing, the JS stops but I see the pictures. Quite annoying !

    Please advise how I can make it work through Magento as I find your slider really great!

    Thanks and regards from UAE ๐Ÿ™‚

  28. Hi Mary Lou,

    Its a really nice slider.I managed to implement it on my website.I tried to add the price below the items,i managed to do it, however I want to decrease the distance between the items in the slider and the horizontal navigation black line.Can you please tell me what css to add to implement that?

  29. i really love so much of your slider. It looks beautiful. May i know is any ways i can use it in wordpress? Thank you !

  30. Hi Mary,

    Did you look into making it auto slide? I have been scratching my head for a while, but can’t get it to work… You help would be appreciated!..

    Thanks in advance

  31. Hi ML

    Thanks for the slider and would like to make it auto slide. How would that be possible. Can you please let us know as this is going to be a custom post typed manageable slider with wordpress.

    Thanks in advance.

  32. Hello, I want to set up that slider on my wordpress site. Can you help me how to upload all the source code?

    thank s in advnce

  33. Hi there! Awesome slider! I’m trying to figure out how I can link a url and it jump straight to one of the slider tabs and not to the first tab. I wondered if anything could help me? I also found this JS that I was trying to work into my problem but with no luck.

    Hope someone can help!! Thank you!!

  34. Great! Thanks Mari Lou ๐Ÿ™‚

    I try use your slider on smartphones (Iphone5 on chrome and safari) but the slider dont work, really the slider don’t is showed…. :/

  35. I love this slider.

    I was wondering if there is a way to make it auto advance.

    I see that someone said to “Try writing JS for it which doesnโ€™t need any trigger!”

    Not being much of a JS person, I am not sure what that means.

    Any help would be greatly appreciated.


  36. Hi Mary Lou, thanks for this slider… I need help… How to make Autoplay on this slider?

    Thanks in advance ๐Ÿ™‚

  37. Auto-Sliding: Not clean, but worked for me.

    jquery.catslider.js LINE 49 (after: var self = this;)
    Add this:

    var changeTime = 5000; var actualCat = 0 setInterval(function() { self.showCategory(actualCat); if(actualCat < $('#mi-slider ul').length - 1) { actualCat++; }else { actualCat = 0; } }, changeTime);

  38. I was wondering how to add six items in the slider seems to show over flow content… can you assist

  39. this slider is so stylish and perfect ! I want to implement it at wordpress website – but i have a huge prob…(( I cant find any links or tips how to place all this info to wordpress.
    Thank you for your inspiration posts – i was just passer by searching for ideas for a small e-shop and now am inspired and want to learn !

  40. Very nice. Is there a way to implement this so that the products are generated dynamically? I want each li to be the latest item in that particular category, as opposed to having to manually update links to each product.

  41. Does anyone know how to implement more than 4 products per category?
    Would be very useful!

    Awesome tutorial!

  42. It does not work. It gets displayed but slide does not work. Even the opacity does not work in the desktop, it only works if I decrease the size of the window.