fixed background effect
March 25, 2015 | 31 Feedbacks

Fixed Background Effect

A simple template that takes advantage of the background-attachment CSS property to create a fixed background effect.
Browser support
  • ie
  • Chrome
  • Firefox
  • Safari
  • Opera

Sometimes you don’t need crazy javascript code to come up with creative and nice looking effects. Today’s snippet is all about a single CSS property: background-attachment. You can set the background to be fixed within the viewport (background-attachment: fixed;). We’ve used this already in our alternate fixed & scroll backgrounds template.

The new trick here is having the same element (in this case a phone) in the exact same position in all background images, so that while you scroll everything moves, but the phone.

The only assets you need are different images, with the same size and an element in common in the same position.

assets needed


Image credits: Unsplash.

iPhone mockup from Pixel Buddha.

Creating the structure

The HTML structure is pretty basic: each section contains a .content element with the title and paragraph. Classes .img-1, .img-2 etc are used to set up different background images in CSS. The .cd-vertical-nav is the arrow navigation (visible on bigger devices only). Data types have been used to identify in jQuery the sections/items of the slider.

Adding style

Couple of important things to bear in mind: iOS devices don’t like background-attachment: fixed; Therefore on small devices the fixed background effect won’t be visible. Also on small devices we don’t use CSS background-images yet, but we inject smaller photos of phones as ::after pseudo-elements of the .cd-content element.

Secondly, since we are using background-images, we can’t have 100% control on where the fixed element (phone in this case) will appear. This is hard to accept if you’re obsessed by pixel perfection, but I couldn’t find a solution for that.

This is all the code you need to achieve the fixed background effect:

Events handling

We used jQuery to implement a basic slider to navigate through the different sections (previous/next arrows and keyboard navigation). On window scroll, we update the arrows visibility (checkNavigation() function) and detect the section visible in the viewport (the .is-visible class is assigned using the checkVisibleSection() function). The nextSection() and prevSection() functions are used to navigate to the next/previous section.


Mar 25, 2015
  • Resource released by CodyHouse

Sebastiano Guerriero

UI/UX designer, with a huge passion for Nutella. Co-Founder of CodyHouse. You can follow him on Twitter or Dribbble.

  • Taimur Aziz

    Awesome work as usual .. Codyhouse rocks again :)

  • homersapien

    Nice effect, unfortunately there’s quite a lot of flickering in the latest version of Firefox.

    • Dimas A. Pante

      Strange… FF 36.0.4 here, all clear. Mac maybe?

      • redcore

        36.0.4 on Mac here, looks fine to me as well.

  • soajourneys

    Very cool. Thanks for sharing.

  • mgiraldo

    did a similar thing using maps and has an extra JS-powered “embiggen” feature:

    a downside to this “background-attachment: fixed” technique is that it doesn’t work on mobile browsers. my approach to that was to use a fade-out effect. i use JS to detect the browser and adapt the CSS accordingly.

    try the link in both desktop and on iOS/Android to compare

  • kandros

    fantastic, can you please share iphone-less images?

  • atchyut nagabhairava

    Dude it was Awesome… <3
    Thanx alot… :)

  • Richard Bradshaw

    Instead of using window.on(‘scroll resize’), then checking rAF, do it the other way round, that should help with the jankiness – use requestAnimationFrame to to call a function which checks if you have scrolled using the screen offset, then if that’s different to last time, then call your function. In other words, instead of checking when you scroll (which causes 100s of calls), check every 1/60s, but only do something if it’s changed. You could also precompute the places things change once, and then just check that when this triggers – both those would make this much much smoother.

  • Gal Vital

    btw, says hi ;)

  • lavaldi

    Excellent article Sebastian!

    Here is the article translated into Spanish


    • Sebastiano Guerriero

      Hi @lavaldi:disqus, thanks for the update. However according to our terms you can just copy excerpts, not the entire article, and the download button should link to the article page.

  • Steve

    Hi Sebastiano,
    I think this is not going to be possible but do you think there a way of specifying what images appear in the phone? It would be great to use this kind of thing as a ‘Meet the team’ page or ‘Band Members’ that kind of thing.
    Therefore it would be great if it could be configurable rather than ‘hard coded’ in Photoshop.
    Maybe HTML6 and CSS4? :)

    • Steve

      CSS3 does have multiple backgrounds (with positioning) but I can’t get it to work. I’m not great with CSS though. Is it possible?

  • francesco_manciocchi

    Hello, great tutorial :) Is there a way to make it work inside a specific div? I mean: using as viewport the div height instead of the browser window height? I have replaced the code as follows but I am having problems with the arrow back/for navigation…Thanks in advance


    var animating = false;

    //update arrows visibility and detect which section is visible in the viewport


    $(‘.portfolio’).on(‘scroll resize’, function(){

    (!’.portfolio’.requestAnimationFrame) ? setSlider() : ‘.portfolio’.requestAnimationFrame(setSlider);


    //move to next/previous section clicking on arrows

    $(‘.portfolio-vertical-nav .portfolio-prev’).on(‘click’, function(){



    $(‘.portfolio-vertical-nav .portfolio-next’).on(‘click’, function(){



    //move to next/previous using the keyboards


    if( event.which==’38’ ) {



    } else if( event.which==’40’ ) {





    //go to next section

    function nextSection() {

    if (!animating) {

    if ($(‘.is-visible[data-type=”slider-item”]’).next().length > 0) smoothScroll($(‘.is-visible[data-type=”slider-item”]’).next());



    //go to previous section

    function prevSection() {

    if (!animating) {

    var prevSection = $(‘.is-visible[data-type=”slider-item”]’);

    if(prevSection.length > 0 && $(‘.portfolio’).scrollTop() != prevSection.offset().top) {


    } else if(prevSection.prev().length > 0 && $(‘.portfolio’).scrollTop() == prevSection.offset().top) {





    function setSlider() {




    //update the visibility of the navigation arrows

    function checkNavigation() {

    ( $(‘.portfolio’).scrollTop() $(document).height() – 3*$(‘.portfolio’).height()/2 ) ? $(‘.portfolio-vertical-nav .portfolio-next’).addClass(‘inactive’) : $(‘.portfolio-vertical-nav .portfolio-next’).removeClass(‘inactive’);


    //detect which section is visible in the viewport

    function checkVisibleSection() {

    var scrollTop = $(‘.portfolio’).scrollTop(),

    windowHeight = $(‘.portfolio’).height();


    var actualBlock = $(this),

    offset = scrollTop – actualBlock.offset().top;

    //add/remove .is-visible class if the section is in the viewport – it is used to navigate through the sections

    ( offset >= 0 && offset < windowHeight ) ? actualBlock.addClass('is-visible') : actualBlock.removeClass('is-visible');



    function smoothScroll(target) {

    animating = true;

    $('body,html').animate({'scrollTop': target.offset().top}, 500, function(){ animating = false; });


  • yw


  • Leandro Ruel

    so you like Nutella huh? =p
    awesome tutorial, thanks!

  • Huyang Noe


  • Debra Chiang

    I’m wondering how to do fixed text on one side in a section, while images are scrollable on the other side without the entire section being scrolled before all images are viewed. For example, the “our cases” section from this website: Thank you!

    • Sebastiano Guerriero

      Hi Debra, I’m not sure you can achieve a similar result using this template. The website you’re linking to has a section that gets “position:fixed” for as long as the user is scrolling through the projects. In our case each section follows a standard flow. However that sounds like an idea for a new resource ;)

      • Debra Chiang

        Thank you for the reply :) Will it be possible to see a tutorial on this soon?
        Thanx again!

  • dipak

    Hi Sebastiano Guerriero
    its a great tutorial
    can i add video inside the iphone frame ?

  • sdaw

    Nice tutorial but as a beginner I miss how important elements were set up, like the media-queries and the functions() :(

  • Jon

    Is there a reason why you’re not just using viewport height of 100?

  • Mr.Ross

    Hey There. I am trying to nest this feature with in div tags and it collapses and is not visible. Any thoughts?

  • Rich Haagenson

    Hey thanks for the great tutorial. I wanted to hide the first navigation arrow only in the first section. How would you go about doing this? Thanks

    • Claudia Romano

      Hi Rich,
      in the checkNavigation() function (main.js file), on line 56, try replacing:


      That should do it!

      • Rich Haagenson

        Works like a charm, thanks :)

  • Anatoly Halizev

    I don’t understand this trick with “.cd-fixed-background .cd-content::after” And how “content: ”;” produces mobile phone picture?

  • shall1987

    Hello, If i add footer to the website it does not work properly when i scroll to the last section footer.It goes fine till footer but when i go up and then it stops on first section and nothing happens. i have div at the top of the section

    Now if i scroll back up to this section the class “is-visible” does not seem to be added on this div.