Square-tailed kite
Square-tailed kiteWhite-tailed kiteHawkOspreySquare-tailed kite

Slider galleries have been around a long time, but until very recently they have almost exclusively been coded using JavaScript. CSS now gives us the ability to create the same effect natively in the browser, without JavaScript or any dependence on frameworks or plugins.

For the purposes of illustration I’ll keep the animation sequence fairly basic; as CSS Filters and variables start to be supported cross-browser, you’ll find that you will be able to add a great many more effects to this simple code.

The principles of the slider are very simple: the elements on the page consist solely of a container (a <header> element, in this case) and a “strip” of slider gallery images side-by-side. The outer container element will act as a window through which we will view each image in the strip sequence.

The Markup

<header>
	<div>
		<img src="square-tailed-kite.jpg" alt="Square-tailed kite">
		<img src="white-tailed-kite.jpg" alt="White-tailed kite">
		<img src="hawk.jpg" alt="Hawk">
		<img src="osprey.jpg" alt="Osprey">
	</div>
</header>

There are just two conditions for the elements:

  1. All the images must be exactly the same size (the photographs I am using in the example have been consistently sized to 800px × 341px with PhotoShop).
  2. The outer container element must be the exact same size as one of the images.

Base CSS

header { 
	border: 1px solid black;
	background: #000;
}
header, div img {
	width: 800px; height: 341px;
}

Viewing the page in a browser, you’ll notice that the images spill outside the <header> element, and that they don’t stretch out in a single horizontal line. Let’s fix that by hiding any content outside the header element:

header { overflow: hidden; }

And make the <div> the width of all the images added together (800 × 4 = 3200 pixels).

div { width:3200px;  }

Showing the next image is simple: we just have to move the <div> the width of one image (800px). You can prove this by altering the CSS momentarily to include the following. (Note that I've dropped vendor prefixes for this example).

div { transform: translateX(-800px); }

The next image in the sequence will be shown, with the images before and after hidden due to overflow: hidden on the header.

Crafting The Animation

As I’ve previously discussed, initializing a CSS3 keyframed animation is very much like using an embedded font: we name and specify the animation at the top of our stylesheet, then call it later in our code.

Animation steps are measured in percentages between the start (0%) and end (100%) frames. The start is easy: we want the slider to begin exactly where it is. We’ll name the animation slider and set that first position:

@keyframes slider {
	0% { transform: translateX(0px); }
}

We need to divide the time for the animation between two actions: displaying an image, and moving to the next one. I’ll make the arbitrary decision to hold each image for 20% of the animation (meaning the image display will take a total of 80% of the total time of the animation). We can now add the second keyframe:

@keyframes slider {
	0% { transform: translateX(0px); }
	20% { transform: translateX(0px); }
}

Because the information remains the same between the first and second keyframe, the <div> doesn’t change. If the total animation was 10 seconds long, the first image would be held for 2 seconds.

We need to move the <div> 800px in 5% of the total animation time, and then hold for another two seconds:

@keyframes slider {
	0% { transform: translateX(0px); }
	20% { transform: translateX(0px); }
	25% { transform: translateX(800px); }
	45% { transform: translateX(800px); }
}

Completing our animation to cover every image is easy: we’re just using multiples of 800px for the movement and intervals of 20% and 5% for the animation:

@keyframes slider {
	0% { transform: translateX(0px); }
	20% { transform: translateX(0px); }
	25% { transform: translateX(800px); }
	45% { transform: translateX(800px); }
	50% { transform: translateX(1600px); }
	70% { transform: translateX(1600px); }
	75% { transform: translateX(2400px); }
	95% { transform: translateX(2400px); }
	100% { transform: translateX(0px);  }
}

We call the animation from the div itself:

div {
	animation-name:slider;
	animation-duration:10s;
	animation-timing-function: ease-in-out;
	animation-iteration-count:infinite;
}

There’s an issue with the animation we’ll discuss in a moment, but right now we’re going to appreciate the fact that the time for the animation sequence is held separately from the keyframes: if you feel the animation is too fast, just modify the value of animation-duration.

Now the issue. As you can see, the <div> moves past the last image, then whips back to the starting position.

Getting Back To The Start

JavaScript has the ability to append elements on the fly: that’s the reason that JavaScript slider animations give the impression of being an endless loop. Such capabilities are beyond the ability of CSS at the moment, so instead we’ll hide the return to the start by adding a copy of the first image at the end.

<header>
	<div>
		<img src="square-tailed-kite.jpg" alt="Square-tailed kite">
		<img src="white-tailed-kite.jpg" alt="White-tailed kite">
		<img src="hawk.jpg" alt="Hawk">
		<img src="osprey.jpg" alt="Osprey">
		<img src="square-tailed-kite.jpg" alt="Square-tailed kite">
	</div>
</header>

You'll need to make the inner <div> wider to accomodate the added image:

div { width:4000px; }

And move the animation to the last image, so that the transition to its exact copy in the first image is invisible:

95% { transform: translateX(2400px); }
100% { transform: translateX(3200px); }

The first and last frames now match, with no motion visible between them, making for a seamless animation.

Pausing The Animation

It’s reasonable – even expected – that the user should be able to pause the animation in order to focus on one image. We can do that with a :hover pseudo-selector and animation-play-state:

div:hover { animation-play-state:paused; }

That pauses the animation, but it would be nice to have some visual indication that it is stopped. We’ll do so with a piece of generated content, triggered from hovering over the <header> element (since the <div> is always moving, it’s a lot easier to position the pause button relative to the <header>). We’ll use two glyphs of the Unicode symbol U+275A, which kind of looks like a pause symbol:

header:after {
	content: "❚❚";
	font-size: 150px;
	position: absolute;
	z-index: 12;
	color: rgba(255,255,255, 0);
	left: 300px; top: 80px;
	transition: 1s all ease-in-out;
}

The pause symbol is invisible by default: we’ll make it visible with a sequence of chained pseudo-selectors:

header:hover:after {
	color: rgba(255,255,255, 0.6);
}

The Future

It would be reasonable to assume that each image would link through to a larger version of itself, or other content. It should be noted that the “slider” <div> could contain any content at all: not just images, but text, videos, iframes, anything. I've also created an enhanced responsive version of the slider.

Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.
Check out the CodePen demo for this article at https://codepen.io/dudleystorey/pen/kuBmw