Fluid CSS3 Slideshow with Parallax Effect

In this tutorial we will create a slideshow with a parallax effect using several CSS3 properties. The idea is to move the background positions of two backgrounds while sliding the container of the slides.
Fluid CSS3 Slideshow with Parallax Effect

From our sponsor: Try Mailchimp today.

In this tutorial, we are going to create a slideshow with a parallax effect with the help of some CSS3 properties. We’ll use radio buttons and sibling combinators for controlling which slide is shown. There will be two backgrounds and the idea is to change the background positions and the position of the slider with transitions in order to create a slight parallax effect.

The graphics used in the demo are by: 5Milli (Global Vector Map) and by WeGraphics (Free Vector Infographic Kit).

Please note: the result of this tutorial will only work as intended in browsers that support the respective CSS properties.

The Markup

We will “connect” the input elements to the division with the class sp-content by using the general sibling combinator. For that we will leave the inputs on the same level like the sp-content div. When we click on an input we will change the background color and background position of it (grid pattern) and also the background-position of the sp-parallax-bg div (the world map) with transitions. The respective slide will be shown by moving the sp-slider ul to the right position. The markup looks as follows:

<div class="sp-slideshow">
	<input id="button-1" type="radio" name="radio-set" class="sp-selector-1" checked="checked" />
	<label for="button-1" class="button-label-1"></label>
	<input id="button-2" type="radio" name="radio-set" class="sp-selector-2" />
	<label for="button-2" class="button-label-2"></label>
	<input id="button-3" type="radio" name="radio-set" class="sp-selector-3" />
	<label for="button-3" class="button-label-3"></label>
	<input id="button-4" type="radio" name="radio-set" class="sp-selector-4" />
	<label for="button-4" class="button-label-4"></label>
	<input id="button-5" type="radio" name="radio-set" class="sp-selector-5" />
	<label for="button-5" class="button-label-5"></label>
	<label for="button-1" class="sp-arrow sp-a1"></label>
	<label for="button-2" class="sp-arrow sp-a2"></label>
	<label for="button-3" class="sp-arrow sp-a3"></label>
	<label for="button-4" class="sp-arrow sp-a4"></label>
	<label for="button-5" class="sp-arrow sp-a5"></label>
	<div class="sp-content">
		<div class="sp-parallax-bg"></div>
		<ul class="sp-slider clearfix">
			<li><img src="images/image1.png" alt="image01" /></li>
			<li><img src="images/image2.png" alt="image02" /></li>
			<li><img src="images/image3.png" alt="image03" /></li>
			<li><img src="images/image4.png" alt="image04" /></li>
			<li><img src="images/image5.png" alt="image05" /></li>
	</div><!-- sp-content -->
</div><!-- sp-slideshow -->

The list elements are the wrappers for each slide and although we are using simply images here, you can use any kind of content.


We’ll set the width of the main container to 80% and set the width of the divisions with class sp-content and class sp-parallax-bg to 100%. The sp-content div will have a background color and a background image (grid) that we will move whenever we slide the slider ul. The sp-parallax-bg div will have a map as background image and we will also move the background position.

.sp-slideshow {
    position: relative;
	margin: 10px auto;
	width: 80%;
	max-width: 1000px;
	min-width: 260px;
	height: 460px;
	border: 10px solid #fff;
	border: 10px solid rgba(255,255,255,0.9);
    box-shadow: 0 2px 6px rgba(0,0,0,0.2);

.sp-content {
    background: #7d7f72 url(../images/grid.png) repeat scroll 0 0;
	position: relative;
	width: 100%;
	height: 100%;
	overflow: hidden;

.sp-parallax-bg {
    background: url(../images/map.png) repeat-x scroll 0 0;
    background-size: cover;
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	overflow: hidden;

The styles of the inputs and the labels:

.sp-slideshow input {
    position: absolute;
	bottom: 15px;
	left: 50%;
	width: 9px;
	height: 9px;
	z-index: 1001;
	cursor: pointer;
    opacity: 0;

.sp-slideshow input + label {
    position: absolute;
    bottom: 15px;
	left: 50%;
    width: 6px;
	height: 6px;
	display: block;
	z-index: 1000;
	border: 3px solid #fff;
	border: 3px solid rgba(255,255,255,0.9);
    border-radius: 50%;
    transition: background-color linear 0.1s;
.sp-slideshow input:checked + label {
	background-color: #fff;
    background-color: rgba(255,255,255,0.9);

.sp-selector-1, .button-label-1 {
    margin-left: -36px;

.sp-selector-2, .button-label-2 {
    margin-left: -18px;

.sp-selector-4, .button-label-4 {
    margin-left: 18px;

.sp-selector-5, .button-label-5 {
    margin-left: 36px;

We’ve set the opacity of the inputs to 0 so that they are not visible. The labels are under the radio button and we will make it look like a little circle. All the inputs and labels will be positioned absolutely and we will place them next to each other by applying a left margin.

Next, we will style the arrows which are simply labels with the respective for attribute. Note, that clicking on a label to active an associated input might not work in mobile browsers. But anyway, you can navigate using the dots since we are actually clicking on the inputs.

The arrow labels have the following style:

.sp-arrow {
    position: absolute;
	top: 50%;
	width: 28px;
	height: 38px;
	margin-top: -19px;
	display: none;
	opacity: 0.8;
	cursor: pointer;
	z-index: 1000;
	background: transparent url(../images/arrows.png) no-repeat;
    transition: opacity linear 0.3s;
	opacity: 1;
	margin-top: -18px;

Now, let’s define when each arrow is shown. On the first slide we, for example, don’t want to show the left arrow. And on the last slide we don’t want to show the right arrow:

.sp-selector-1:checked ~ .sp-arrow.sp-a2,
.sp-selector-2:checked ~ .sp-arrow.sp-a3,
.sp-selector-3:checked ~ .sp-arrow.sp-a4,
.sp-selector-4:checked ~ .sp-arrow.sp-a5 {
    right: 15px;
	display: block;
	background-position: top right;
.sp-selector-2:checked ~ .sp-arrow.sp-a1,
.sp-selector-3:checked ~ .sp-arrow.sp-a2,
.sp-selector-4:checked ~ .sp-arrow.sp-a3,
.sp-selector-5:checked ~ .sp-arrow.sp-a4 {
    left: 15px;
	display: block;
	background-position: top left;

When an input is selected, the sp-content div will have a transition for the background-position and the background-color. The second transition is going to take a bit longer:

.sp-slideshow input:checked ~ .sp-content {
    transition: background-position linear 0.6s, background-color linear 0.8s;

The div with the world map (sp-parallax-bg) will also have a transition for the background-position:

.sp-slideshow input:checked ~ .sp-content .sp-parallax-bg {
    transition: background-position linear 0.7s;

In this way we can add a background parallax effect.

Let’s define the changes to color and background-position for the sp-content div:

input.sp-selector-1:checked ~ .sp-content {
    background-position: 0 0;
	background-color: #727b7f;

input.sp-selector-2:checked ~ .sp-content {
    background-position: -100px 0;
	background-color: #7f7276;

input.sp-selector-3:checked ~ .sp-content {
    background-position: -200px 0;
	background-color: #737f72;

input.sp-selector-4:checked ~ .sp-content {
    background-position: -300px 0;
	background-color: #79727f;

input.sp-selector-5:checked ~ .sp-content {
    background-position: -400px 0;
	background-color: #7d7f72;

… and the sp-parallax-bg div:

input.sp-selector-1:checked ~ .sp-content .sp-parallax-bg {
    background-position: 0 0;

input.sp-selector-2:checked ~ .sp-content .sp-parallax-bg {
    background-position: -200px 0;

input.sp-selector-3:checked ~ .sp-content .sp-parallax-bg {
    background-position: -400px 0;

input.sp-selector-4:checked ~ .sp-content .sp-parallax-bg {
    background-position: -600px 0;

input.sp-selector-5:checked ~ .sp-content .sp-parallax-bg {
    background-position: -800px 0;

The unordered list with the class sp-slider will have a width of 500%. This is because we have 5 slides. It will have a transition for the left value, that we will change depening on the input that is checked:

.sp-slider {
    position: relative;
	left: 0;
    width: 500%;
	height: 100%;
	list-style: none;
    margin: 0;
	padding: 0;
    transition: left ease-in 0.8s; 

Each list element is a slide and it will also have a transition for the opacity. We will give both, the slide and the image inside the box-sizing property of “border-box”. This will allow us to set a padding but also use 100% values for heights and widths and not worry about any overflow:

.sp-slider > li {
	color: #fff;
	width: 20%;
	box-sizing: border-box;
	height: 100%;
	padding: 0 60px;
    float: left;
	text-align: center;
	opacity: 0.4;
    transition: opacity ease-in 0.4s 0.8s; 
.sp-slider > li img{
	box-sizing: border-box;
	display: block;
	margin: 0 auto;
	padding: 40px 0 50px 0;
	max-height: 100%;
	max-width: 100%;

Now we need to set the correct left values for each selected slide:

input.sp-selector-1:checked ~ .sp-content .sp-slider {
    left: 0;

input.sp-selector-2:checked ~ .sp-content .sp-slider {
    left: -100%;

input.sp-selector-3:checked ~ .sp-content .sp-slider {
    left: -200%;

input.sp-selector-4:checked ~ .sp-content .sp-slider {
    left: -300%;

input.sp-selector-5:checked ~ .sp-content .sp-slider {
    left: -400%;

Each current slide will then get opacity 1:

input.sp-selector-1:checked ~ .sp-content .sp-slider > li:first-child,
input.sp-selector-2:checked ~ .sp-content .sp-slider > li:nth-child(2),
input.sp-selector-3:checked ~ .sp-content .sp-slider > li:nth-child(3),
input.sp-selector-4:checked ~ .sp-content .sp-slider > li:nth-child(4),
input.sp-selector-5:checked ~ .sp-content .sp-slider > li:nth-child(5){
	opacity: 1;

That’s all, hope you like it!

Tagged with:

Rey Wang

Rey is a digital product designer based in Beijing, China. Contact & collaboration will be welcome.


Stay up to date with the latest web design and development news and relevant updates from Codrops.

Feedback 59

Comments are closed.
  1. OMG!!!! this latest tutorials of CSS3 ore awesome! thank’s a lot! I learned so much.

  2. These CSS3 tutorials are helping me to look at the functions that CSSS3 can give to a JavaScript free webpage =D

  3. I really like the use of parallax for content sliders.
    I wish there were less hacky way (the radio input part) to do this without JS though.

    You should really run ImageOptim or something similar on your images before putting them on the web. I made the exercise and saved 56%, or 210kb on the size of the 5 pngs used in the demo. I started with 387kb of images and ended with 173kb…lossless. The result for the 5 images is equivalent to the size of the original background map and first image.

    It may not seem like much but on a larger scale, especially websites that target mobiles, this is a good habit to have. Save your users the extra bandwidth !

  4. Great work!

    Just an issue I am having. I am trying to add a 6th and 7th slide.

    I have been able to add the the 6th and 7th button but I am unable to add content to it?

    Any suggestions?

    • Take note of heights of the list item, the percentage of the heights are 6/100 or 7/100. Just find an approximate value.

  5. The buttons(clicking) do not work on an iPhone. Is there something I’m missing?

  6. I was having trouble with this and checked the downloaded HTML and CSS files. Looks like there’s a lot of CSS that isn’t included on the web page example that is included in the download. This is my first Codrops attempt so maybe that’s normal, but it would help to know ahead of time that it’s not going to work exactly as planned because there’s more work to do in the CSS. I was using Chrome on Mac, and writing in TextWrangler.

    • Hi Lela, please note that the tutorials on Codrops usually don’t include vendor prefixes. You’ll have to include those in the CSS (or download the ZIP). Hope it helps, cheers, ML

  7. Yeah, I guess I just have to keep checking the list under borders and transitions for special cases. Maybe knowing when there should be vendor prefixes included will be more obvious after I try more tutorials. It doesn’t seem obvious now.

  8. Ok, went through and added everything that I noticed – but won’t work correctly for me. The “.sp-slidesnow input + label”, somewhere around line 45, causes the most problems by hiding the main image. I just end up with a grey box with a white border, all set on a similar grey background.

  9. Sorry, what is this “~” operator used for? It’s not something I’ve come across yet in CSS3

    • It’s the sibling selector. It selects all the siblings that match the next selector. In this case, it selects the next .sp-content div, if it is a sibling of the input.

  10. I managed to set a sixth and seventh button but the issue is when they are both checked they do not show anything. What did I do wrong????

    actually i need more than 7 check buttons but these buttons that i managed to set are not showing anything.

    Help please!!!!!!!!!!!