Among the many super nice design features of the Yahoo! Weather app for iOS is the transition between city screens. The background image doesn’t just move away as the screen moves from one screen to the next, the background image itself slides. It appears to be hiding some of the “old” screen and revealing more of the “new” screen those closer you have it to being in full view.
Let’s try and pull it off on the web.
The HTML
Like any slider, there are three main components:
- The container that holds everything into shape
- A sliding container that is as wide as all the slides in a row
- Each individual side container
We won’t bother too much with content inside the slide. I’ll just add the temperature to show each slide can indeed hold content on top.
<div class="slider" id="slider">
<div class="holder">
<div class="slide" id="slide-0"><span class="temp">74°</span></div>
<div class="slide" id="slide-1"><span class="temp">64°</span></div>
<div class="slide" id="slide-2"><span class="temp">82°</span></div>
</div>
</div>
The container might be a <section>
, slides might be <article>
. It really depends. I’ll let you make the semantic choices for your own needs.
The layout plan is like this:

The CSS
The “slider” (visual container) and the slides need to have explicity the same size. We’ll use pixels here but you could make it work with anything.
.slider {
width: 300px;
height: 500px;
overflow-x: scroll;
}
.slide {
float: left;
width: 300px;
height: 500px;
}
Floating those slides to the left isn’t going to make them line up in a row, because the parent element of the slides isn’t wide enough to let them do that. That’s one of the reasons we need the holder element. It will be 300% wide (Num. of slides × 100%) which will fit three slides exactly.
.holder {
width: 300%;
}
Each one of our slides has a unique ID. This is useful because, if we choose, we can create anchor links that link to those ID’s and the slider will “jump” to those slides. We’ll add JavaScript to do some actual “sliding”, but our slider will work even without that. ID’s make that possible, so let’s use them here to drop in some lovely background images.
#slide-0 {
background-image: url(http://farm8.staticflickr.com/7347/8731666710_34d07e709e_z.jpg);
}
#slide-1 {
background-image: url(http://farm8.staticflickr.com/7384/8730654121_05bca33388_z.jpg);
}
#slide-2 {
background-image: url(http://farm8.staticflickr.com/7382/8732044638_9337082fc6_z.jpg);
}
With all this in place, our layout comes into shape:

The CSS (black fading)
Just as a small detail, the temperature set in white may be in danger of not being visible depending on the photo behind it. To ensure that it is, we can make the photo subtly fade to black toward the bottom. A pseudo element will do nicely.
.slide:before {
content: "";
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 40%;
background: linear-gradient(transparent, black);
}
A picture explanation is in order here:

The JavaScript (background sliding)
We’re going to use jQuery here because we love life. Our goal is the adjust the background-position of the slides as we scroll. We can set background-position in percentages in CSS, but that alone doesn’t do the cool hide/reveal more effect we’re looking for. Based the amount scrolled (which we can measure in JavaScript), we’ll adjust the background-position. Alone, that would look something like this:
$("#slider").on("scroll", function() {
$(".slides").css({
"background-position": $(this).scrollLeft()/6-100+ "px 0"
});
});
The “6” and “-100” in there are magic numbers. Not CSS magic numbers that are prone to frailty, but traditional magic numbers. Just some numbers that happen to make the effect work. A bummer, perhaps, but not that big of a deal. Design-y things are sometimes like that. Perhaps best to leave a comment in the code to that effect. These particular numbers are based on the images I used and their size and what looked good.
The effect here is the background shifting we’re after:

The JavaScript (imparting structure)
That little snippet of JavaScript looks lonely up there without any structure behind it. A slider is a great excuse to look at a simple way to structure JavaScript.
We can make everything slider-related one object.
var slider = {
};
Then we’ll group up the related elements into one area, bind our events together, and write little functions that do very specific things.
var slider = {
el: {
slider: $("#slider"),
allSlides: $(".slide")
},
init: function() {
// manual scrolling
this.el.slider.on("scroll", function(event) {
slider.moveSlidePosition(event);
});
},
moveSlidePosition: function(event) {
// Magic Numbers
this.el.allSlides.css({
"background-position": $(event.target).scrollLeft()/6-100+ "px 0"
});
}
};
slider.init();
The HTML (adding navigation)
Adding swipe stuff would be super (super) sweet (hint). But for now let’s add little press-able links to change slides, rather than relying on the scrollbar. You might even remove the scrollbar in real life (straight up overflow: hidden; on the container). What we need is anchor links that link to the ID’s of the individual slides.
<nav class="slider-nav">
<a href="#slide-0" class="active">Slide 0</a>
<a href="#slide-1">Slide 1</a>
<a href="#slide-2">Slide 2</a>
</nav>
Style those as you will. For the demo, I make them little tiny gray circles with the text hidden.
The JavaScript (adding navigation)
Our structure is more useful now. We simply add a few more elements we’re dealing with, add a new event we’re watching for (clicks on nav), and write a little function to deal with that event.
We know how far to animate the scroll position when a nav link is clicked from the ID on the link itself. The link might be href=”#slide-1″, which we can get “1” from easily. Then the position we need to scroll to is (1 × width of slide), so 300 in our case. We’ll store that 300 value right in the JavaScript.
var slider = {
el: {
slider: $("#slider"),
allSlides: $(".slide"),
sliderNav: $(".slider-nav"),
allNavButtons: $(".slider-nav > a")
},
timing: 800,
slideWidth: 300, // could measure this
init: function() {
// You can either manually scroll...
this.el.slider.on("scroll", function(event) {
slider.moveSlidePosition(event);
});
// ... or click a thing
this.el.sliderNav.on("click", "a", function(event) {
slider.handleNavClick(event, this);
});
},
moveSlidePosition: function(event) {
// Magic Numbers
this.el.allSlides.css({
"background-position": $(event.target).scrollLeft()/6-100+ "px 0"
});
},
handleNavClick: function(event, el) {
// Don't change URL to a hash, remove if you want that
event.preventDefault();
// Get "1" from "#slide-1", for example
var position = $(el).attr("href").split("-").pop();
this.el.slider.animate({
scrollLeft: position * this.slideWidth
}, this.timing);
this.changeActiveNav(el);
},
changeActiveNav: function(el) {
// Remove active from all links
this.el.allNavButtons.removeClass("active");
// Add back to the one that was pressed
$(el).addClass("active");
}
};
slider.init();
We have an “active” class on the nav links just to use in CSS to visually indicate which slide is active. We handle that by removing “active” from all links and then adding it back to the one that was clicked.
Demo!
Check out this Pen!
This is friggin’ sweet!
The next thing I’m thinking about now is if there is a way to dynamically pull in flicker photos.
I wish I could “LIKE” this haha.
Flickr offers an API: http://www.flickr.com/services/api/
This is great! Touch and swipe detection would be the perfection touch (pun intended).
Simple, it’s great. Thanks!
Oh, that’s really nice. Great tutorial.
Just tested on windows phone, touch slide works well… thanks!
A wonderful delicious and subtle effect. Thanks for sharing. Unfortunately, I did not totally understand the concept of the magic numbers. I hope someone can enlighten me.
‘Magic Numbers’ are numbers that aren’t programmatically found- you just have to play around with them and find out which ones make the effect that you need. In this case the effect just looks better (to the designer) to set the element background position to 100 less than the distance from the left (.scrollLeft()) divided by 6.
This is great. You can even add extra images very easily by just making few changes. But do you think there is a way to force the images to automatically center in the frame when sliding them in a touch-screen device? That would make it perfect.
Thank you for your hard work on providing us with great articles.
This is a wonderful tutor… I love it.
Setting the width of the holder as a percentage (300%) of the container is great. You can do the same for the slides — if you have three slides, each slide will be 33.333…% of the holder. Use enough significant digits to allow subpixel precision on your target device resolution, and overflow (sum greater than 100%) won’t happen.
A better solution for this, that will allow it to be flexible (in design and content)
.slider {
height: 100%;
overflow-x: scroll;
-webkit-overflow-scroll: touch;
.holder {
height: 100%;
white-space: nowrap
}
.slide {
display: inline-block
width: 100%;
height: 100%;
}
There, you can have as many items as you want. If you don’t want it full screen then add fixed values to .slider.
A solution without jQuery that also supports automatically scrolling to the “active” one: http://codepen.io/Merri/pen/fdAaI
The JavaScript is well commented, but you need to know your closures well to understand why it works.
This is exactly what I was talking about.
thanks Vesa Piittinen.
I have now refactored the code a bit as well as added new features such as mouse wheel support and a fade in effect. The code now always knows the current slide and sets active class on it. Did some optimizations but the mix of effects can be a bit hard on some devices (box-shadow and text-shadow together with a possibility for multiple levels of opacity).
Very nice, Vesa. I’m assuming you support swipe events. I am not near a device that supports it.
Hi Vesa,
I just tested out your codepen on a nexus 7 and it worked quite well, it was a bit laggy but that could be due to all the effects you mentioned. However for some reason I was unable to swipe on the second slider, but could on the first. Something to do with having links on the first and not the second?
Chris,
Having done a lot of similar stuff recently, I have realised that a cleaner alternative to
Is simply
Where your anchor might be
Obviously this alternative is only slightly better in the context of the tutorial, but once you have lots of JavaScript or complex situations, doing things this way makes life a lot easier.
Everything else was perfect. This is a great tutorial.
var position = $(el).attr("data-index");
could also be simplified down to
var position = $(el).data("index");
Oliver, the jQuery data method isn’t the same as using HTML5 data attributes.
http://stackoverflow.com/questions/7261619/jquery-data-vs-attr
Ironically, I’ve been working on a jQuery “plugin” for lazy-loading background images. https://gist.github.com/yramagicman/5602693
Currently, this doesn’t work with scrolling, but I’m working on that.
You can allways avoid the magic numbers. But it takes a little more time to figure out where
” $(event.target).scrollLeft()/6-100 ” comes from. I actually did something similar today. I used jQuery to get viewport width, element width with margin and stuff. And ended up with unpretty equation… but.. no magic numbers and working responsively :)
You can use this java script in order to save 41 % of the original size, its kind of make it faster.
var slider={el:{slider:$("#slider"),allSlides:$(".slide"),sliderNav:$(".slider-nav"),allNavButtons:$(".slider-nav > a")},timing:800,slideWidth:300,init:function(){this.bindUIEvents()},bindUIEvents:function(){this.el.slider.on("scroll",function(a){slider.moveSlidePosition(a)});this.el.sliderNav.on("click","a",function(a){slider.handleNavClick(a,this)})},moveSlidePosition:function(a){this.el.allSlides.css({"background-position":$(a.target).scrollLeft()/6-100+"px 0"})},handleNavClick:function(a,b){a.preventDefault(); var c=$(b).attr("href").split("-").pop();this.el.slider.animate({scrollLeft:c*this.slideWidth},this.timing);this.changeActiveNav(b)},changeActiveNav:function(a){this.el.allNavButtons.removeClass("active");$(a).addClass("active")}};slider.init();
Awesome graphics, however what is the best word document to edit the CSS code? I am using Notepad, which does work well. Any ideas would be appreciated.
Wow! That is such a neat effect. Love it!
well written code…. just tried with adding some more pictures to slider and it worked perfectly fine..
Looks simply great, thanks.
just beautiful
Great writeup as usual Chris. Thanks!
Great article and example. Could anyone tell me how to make that slider starts to move all alone and stops when mouse is over the image? Thanks in advance.
This is really cool, I specially like how you always challenge yourself on doing this kind of experiments for the sake of it! Thank you!
Well, once again getting inspired by CSS tricks… After the tiredness of a very long days working on forms and sometimes pointless campaigns your stuff bring fresh air and ideas that I can’t wait to try… And nice to see how apply Object literal notation on JS between among of other good stuff, THANKS FOR SHARING
Very nice, there are a lot of css tricks but this one is simple but amazing.. :)
Nice tutorial but I couldn’t get the anchors to work and the layout was a bit messed up until I added “position: relative” to the slides (.slide), I guess this is as a result of the overlayed gradient element.
Sexy bit of code here!
i have seen this be done on websites backgrounds…
Thanks for I was for something to make my background slide a bit,
css-tricks rocks
I guess is better set overflow-x option to hidden.
YES!!! This is what I was wondering how to hide that clunky scrolling bar in below. THANK YOU, Andrés.
Looks super cool. Touch swipe makes it most awesome.
Hey there, nice stuff! Works great, but when I change the dimensions to (example) 900 width and 300 height, it will be kinda messy. Does it have to do with the 6-100?
This is super sexy. Swipe works on my Android 2.3.6 phone running Firefox. I think I’ll be using this when I redesign my site. :-)
Why slide backgrounds instead of images? Should be easy enough to do the same with img tags and position instead.
Fork it and do it.
I’ve always just gone with the rule that if it serves as a background image, it goes as a CSS rule and I store the image in a folder inside css/images. If it’s a content image (a logo, product, gallery picture, etc), it gets put inside an img tag. It’s kind of nitpicky but it’s how I learned to keep my filesystem organized and it makes semantic sense to me.
In this case, the images are being used as a background image, so why would you want to put it in an image tag and add extra CSS for positioning when you could otherwise have a natural flow to your markup?
If i copy and paste the HTML code so it appears twice, When i use the scroll bar on the first slider, it also moves the 2nd slider. but not the other way round.
If I have 3 sliders, scrolling the first will move all 3, but scrolling the 2nd, won’t move the 3rd.
This is great! I’m going to use this to replace the RoyalSlider I’m using. I wonder how easy it would be to add “Prev” & “Next” buttons for navigation…
Curious, will the parallax effect break if this is converted to use percentages? ( 100% Screen width )
I am very new to Javascript / Jquery and stumbled across this tutorial and thought I would give it a go.
So I copied and pasted the HTML, CSS and JS code provided in the Demo, the HTML and CSS work fine but I just can’t get the JS to work or figure out why it is not working, it has to be something I am doing (or not doing) in the HTML as all the code clearly works in the Demo.
At the moment I am trying to use an external JS file by using the below code in the head section on the HTML. Like I say I am very new to this and this and have probably got this all wrong.
Could anyone offer some advise to a noob like me to get this working?
I’m having the same problem, everything except the javascript works fine no matter if I put the code in the header or link to a .js file. Anything know what could be going wrong?
I believe you need to import the jQuery library in the head of your HTML document for the JS file to work properly by either linking to the latest hosted version or by downloading it and then putting it up on your web server.
Here are the instructions for both methods along with downloadable files.
Thanks for very good explain.
This is great! it works perfectly for me.
Now I want to add 2 more features to this:
1) Nav bullet can be updated following touch scrolling (this is no problem with clicking at bullets).
2) Auto playing.
If anyone knows how to do these, please advise. Thanks ~
At my web design agency, we would never create endless scrolling pages of content. This is a very outdated and non creative way of modern website design.
Hi Chris, Thanks for this great code snippet. One thing I noticed – when I copy the code from CODEPEN and re-create the files locally – somehow the smoothness gets lost? What did I do wrong?
THX for help.
I had the same problem for hours until I uploaded it on my internet server and then the js’s smooth sliding function worked gracefully! (Although, the different size of my photos is another story. :/ )
hey Chris, this looks awesome (on the result), but when i tried it my self, the java script doesn’t work with me, i think the problem that i don’t know how to work with java script, could you please type in the code necessary for including the css and js files.
Hello again, after adding the following two line of code, everything works fine, it is really awesome !! thanks. So far so good, still want something, how to make this (the sliding) happened automatically after, let say 10 seconds !!
“two line of code” which is…?
Hey Chris
I’m just trying to get this code to work. The html and css are working fine however I can’t seem to get the sliding animation working on the images. All I can think is that I’m not referencing the correct jQuery source. What links would I have to include in order to get the jQuery library loaded?