skip to content

CSS: Fading slideshow with a touch of JavaScript

 Tweet0 Tweets

Thanks mainly to WordPress, every other website now has some kind of carousel or slideshow on the homepage. That is despite all advice to the contrary.

Regardless of the merits, these features also tend to use bloated JavaScript and jQuery plug-ins adding hundreds of extra kilobytes to the homepage - slowing things down and damaging SEO on the most important page of your website.

Here we present a simple slideshow which by contrast requires less than 1kb of CSS and JavaScript.

Setting up the HTML

The starting point is to set up a list of images on the page, wrapped in a DIV. Something like the following:

<div id="stage"> <a href="1.jpg"><img src="1.jpg" width="360" height="270"></a> <a href="2.jpg"><img src="2.jpg" width="360" height="270"></a> <a href="3.jpg"><img src="3.jpg" width="360" height="270"></a> <a href="4.jpg"><img src="4.jpg" width="360" height="270"></a> <a href="5.jpg"><img src="5.jpg" width="360" height="270"></a> <a href="6.jpg"><img src="6.jpg" width="360" height="270"></a> <a href="7.jpg"><img src="7.jpg" width="360" height="270"></a> <a href="8.jpg"><img src="8.jpg" width="360" height="270"></a> </div>

In this example all the images have links, but that's not necessary. If you remove the links, you'll just need to change some of the CSS and JavaScript to reference 'img' instead of 'a'. Later we'll look at placing text over the images for a caption, call to action (CTA) or similar.

An improvement would be to have only two images on the page load time, and then post-load and insert the others into the DOM as required. That saves having to load them all at once, making the page faster and boosting SEO.

If we stop here, our images are just going to wrap across the page. What we need is some styles to prevent that from happening.

CSS to stack the images

Here is the CSS we are using for the demonstration below:

<style type="text/css"> #stage { margin: 1em auto; width: 382px; height: 292px; } #stage a { position: absolute; } #stage a img { padding: 10px; border: 1px solid #ccc; background: #fff; } #stage a:nth-of-type(1) { animation-name: fader; animation-delay: 4s; animation-duration: 1s; z-index: 20; } #stage a:nth-of-type(2) { z-index: 10; } #stage a:nth-of-type(n+3) { display: none; } @keyframes fader { from { opacity: 1.0; } to { opacity: 0.0; } } </style>

By setting the links to position: absolute we're taking all the images out of the document flow and stacking them on top of one another. We then need to assign a width and height to #stage to reserve space on the page for the slideshow. This equals the image dimensions plus padding (10px on each side) and borders (1px on each side).

We then use some nth-of-type selectors to place the first image on top of the stack, the second image just behind, and the rest hidden from display.

Finally, we assign an animation keyframe to the top image telling it to wait four seconds before fading out to opacity: 0. All that's missing now is a touch of JavaScript to move the faced image to the bottom of stack so the next image can be displayed and fade out in turn.

A touch of JavaScript

All that is needed here is to assign an event handler to our images that is triggered when the keyframe animation ends. It takes the foremost photo, and moves it to the back. Much as you would do with a pack of playing cards:

<script type="text/javascript"> // Original JavaScript code by Chirp Internet: // Please acknowledge use of this code by including this header. window.addEventListener("DOMContentLoaded", function(e) { var stage = document.getElementById("stage"); var fadeComplete = function(e) { stage.appendChild(arr[0]); }; var arr = stage.getElementsByTagName("a"); for(var i=0; i < arr.length; i++) { arr[i].addEventListener("animationend", fadeComplete, false); } }, false); </script>

The new uppermost image now assumes the nth-of-type(1) properties, including the fader keyframe, and so on through the other images.

And that's it! No bloated code, no plugins, no libraries, just a few lines of vanilla JavaScript which works in all modern browsers.

To support older browsers, there are browser prefixes for WebKit, Mozilla and Microsoft and you can find details in our earlier article on constructing an Infinite Animated Photo Wheel.

Working demonstration

Putting it all together you get a simple fading slideshow:

If you want the slideshow to move slower, or faster, or use a different transition, it's just a matter of adjusting the Transition Timing Function (or animation-timing-function in this case). The borders can also be easily re-styled or removed.

Displaying text over the slideshow

There are many ways to go about this, but perhaps the simplest is to add some title attributes to our links and have them displayed over the image using CSS:

#stage a::after { position: absolute; left: 11px; bottom: 11px; padding: 2px 0; width: calc(100% - 22px); background: rgba(0,0,0,0.5); text-align: center; content: attr(title); font-size: 1.1em; color: #fff; }

With no other changes aside from adding titles to our links, we now have the startings of a basic CTA slideshow, which could even be turned into a carousel with a few tweaks to the keyframe.

So if you absolutely have to have a homepage slideshow to please the powers that be, think about ditching jQuery for something like the above. And let us know if you have any feedback or questions using the button below.

Converting to a carousel

A few simple changes to the CSS makes our slideshow behave more like a carousel:


<style type="text/css"> #stage { margin: 1em auto; width: 360px; height: 270px; border: 10px solid #000; overflow: hidden; } #stage a { position: relative; display: inline-block; } #stage a::after { position: absolute; top: 50%; left: 0; transform: translateY(-50%); width: 100%; text-align: center; content: attr(title); font-weight: bold; font-size: 2em; color: #fff; text-shadow: -1px -1px 0 #333, 1px -1px 0 #333, -1px 1px 0 #333, 2px 2px 0 #333; } #stage a:nth-of-type(2) { left: 360px; top: -50%; animation-name: slider; animation-delay: 4s; animation-duration: 1s; animation-timing-function: cubic-bezier(0,1.5,0.5,1); } #stage a:nth-of-type(n+3) { display: none; } @keyframes slider { from { transform: translateY(-50%) rotate(30deg); left: 360px; } to { transform: translateY(-50%); left: 0px; } } </style>


<script type="text/javascript"> // Original JavaScript code by Chirp Internet: // Please acknowledge use of this code by including this header. window.addEventListener("DOMContentLoaded", function(e) { var stage = document.getElementById("stage"); var slideComplete = function(e) { stage.appendChild(arr[0]); }; var arr = stage.getElementsByTagName("a"); for(var i=0; i < arr.length; i++) { arr[i].addEventListener("animationend", slideComplete, false); } }, false); </script>

And of course your first question is going to be how to add navigation spots or arrows over the image...

Working with different size images

If your images differ in size or shape you will see parts of the larger ones protruding from behind the smaller ones. One way around this is to use JavaScript to adjust the padding to make every slide the same dimension:

<script type="text/javascript"> // Original JavaScript code by Chirp Internet: // Please acknowledge use of this code by including this header. window.addEventListener("DOMContentLoaded", function(e) { var maxW = 0; var maxH = 0; var stage = document.getElementById("stage"); var fadeComplete = function(e) { stage.appendChild(arr[0]); }; var arr = stage.getElementsByTagName("img"); for(var i=0; i < arr.length; i++) { if(arr[i].width > maxW) maxW = arr[i].width; if(arr[i].height > maxH) maxH = arr[i].height; } for(var i=0; i < arr.length; i++) { if(arr[i].width < maxW) { arr[i].style.paddingLeft = 10 + (maxW - arr[i].width)/2 + "px"; arr[i].style.paddingRight = 10 + (maxW - arr[i].width)/2 + "px"; } if(arr[i].height < maxH) { arr[i].style.paddingTop = 10 + (maxH - arr[i].height)/2 + "px"; arr[i].style.paddingBottom = 10 + (maxH - arr[i].height)/2 + "px"; } arr[i].addEventListener("animationend", fadeComplete, false); } }, false); </script>

Please note that the above code is for the case where the slideshow contains only images without links around them. If there are links, replace img with a and arr[i] with arr[i].firstChild throughout, with the exception of the last line.



Send a message to The Art of Web:

used only for us to reply, and to display your gravatar.

<- copy the digits from the image into this box

press <Esc> or click outside this box to close

User Comments

Post your comment or question

29 September, 2020

Great image transitions - love it. I want to make the images displayed always fit 100% of the browser window width - how do I do that?

7 September, 2020

Thank you very much for this! I was wondering if there is a way to shuffle the images randomly?

18 July, 2020

Hi. I'm trying to work out how to change the animation delay/duration between different images. Basically I want to stay much longer on the final image before going back to repeat. Can that be done?

Using the code as presented all photos are assigned the same @keyframes animation once they come to the front of the pile (nth-of-type(1)).

To treat a particular image differently, you could assign a class and use CSS to alter the animation parameters:

#stage a.longer:nth-of-type(1) {
animation-duration: 4s;

5 June, 2020

Hi guys,

I got this banner rotation to work on my website fine. However, as I try to center the rotational banners on my leftnavbar of my website, it kind of goes crazy to the center of the page cover other wording.

How can I sort this issue out?

Appreciate it very much. Thanks.

14 April, 2020

The slideshow looks great, just what I need, but the html content below is now hidden underneath it. Any ideas why that is happening? Thanks

7 February, 2020

Great slide show how do i change the position of the slide show i have tried to use float or give it left, right, bottom and top sizes but nothing is working in the CSS. the slideshow just stays i the middle of the page

do you know what i need to change in CSS


What we have on the page works out to:

#stage {
position: static;
margin: 1em auto 1em auto;

which centres the container on the page.

Changing an 'auto' to '0' will align it left or right, and "position: absolute;" will let you put it anywhere.

29 November, 2019

Hi, very interesting site for carousel or slideshow. But is it possible to make the website work from an image folder, instead of naming the images seperatly in html. In know ik can work with php, but can it work locally, so you only have to put the images in the folder, without further coding?
Thank you for your reply.

JavaScript can't access web server directories directly, but you could create a server-side script for that purpose - converting the file list into JSON format and making it accessible via a public URL.

24 January, 2019

Your slider is very cool, the only thing is not clear how to add slide switching points

17 December, 2018

I assume you have to have images that have the same exact dimensions. What if you have images with different heights but the same width - or vice versa? Can you have a slideshow in this case where, for example if you keep the width constant but have different heights in some cases, the image centers itself vertically in the slideshow?

The problem is in the way the fade-out works. Both images have to be visible at the same time, with the top one then fading out. When the queued image is larger, it pokes out from behind the current image.

You could try a CSS solution to fore them to the same height:

#stage img {
max-height: 800px;
width: auto;

But even then you'd want them to be the same shape.

Otherwise you can set larger borders (or margin) on the smaller images to make them cover the same area, and the image centered...

24 February, 2018

Thank you so mutch, i now have a greate website. Do you hate W3schools, i do. This is so mutch easyer! Tanks you again! �™�