A full-screen, centered background image with a centered logo for all browsers

Introduction

I recently tried to add a full screen hero image to a site with a logo sitting in the middle of it. It was way harder than it should have been which made me want to write this article for anyone else trying to achieve it.

Many people will have stumbled across this very useful article on csstricks. It does a great job of documenting a number of methods however I found that most of them did not do everything I wanted. I therefore had to adapt one of their techniques and add some extra bits to it.

Here is what I want to do:
– Have an image of an unspecified ratio that fills the landing screen of the website
– Have a logo centered on top of that image
– Have the remaining content of the page sit below this hero image and show when scrolled
– Have this work within reason on ALL browsers

You can see the final demo of this here and get the full source code here.

Before I start let me just say that any technique that employs background-size: cover does not work in mobile Safari and many Android versions. So for the foreseeable future (like 5 to 10 years) this technique is out for me.

The steps I took in achieving my goal:
1. Create a holder element and make that always fit the viewport
2. Put an image in the holder and make it fill that holder
3. Put a logo on top of the holder and center it vertically and horizontally
4. Add srcset for progressively serving the correct image size to each browser

NOTE: You will need jQuery, so pull that into your page before anything else.

1. Create a holder element and make that always fit the viewport

HTML

<div class="hero-holder">
</div>

CSS

/*------------------------------------------------------------------------------- The BG image holder */

.hero-holder {
  position: relative;
  float: left;
  overflow: hidden;
  width: 100%;
  height: 1024px; 
  background-color: #808080;
}

@media ( min-height: 1025px ) {
  .hero-holder {
    height: 100vh;
  }
}

JS

// Write the setHeight function and save as a variable
var setHeight = function () {
    
    var holderHeight = $(window).height();
    
    $(".hero-holder").css({"height": holderHeight});
    
};
 
// Call the setHeight function on window load
$( document ).ready( setHeight );
 
// Call the setHeight function on wondow resize
$(window).on( "resize", setHeight ).resize();

What I did here
The holder is given a width of 100%. That bit is easy. The hard bit is making the holder fit the height of the viewport. The only way to do this for ALL browsers is using jQuery. So that is what I did.

However, this means the div has a zero height until the DOM is loaded. This is ugly and shows the rest of the page content until the div has been set a height by jQuery. To address this I initially used height: 100vh. This has pretty good support, however fails on Safari 5.1 and down, Android 4.3 and down and IE 8.

To address Safari and Android on small screens I used a height specific media query to set an initial height bigger than the viewport and then to use 100vh which is more elegant for taller screens. This ensures that as little people as possible see the extra page content before the jQuery loads. Yes, this does leave behind IE8 and Safari 5.1 users with a screen height of higher than 1024px, but only until the jQuery kicks in. I, personally, can live with that, you must decide for yourself whether you can too.

But wait. There is a bug here. Mobile iOS 8 and some devices using Android 4.1 have a 60px discrepancy in the height of the div due to the resizing url bar. There are fixes for this here however I have chosen not to use these. The reason being that I want to tailor for the highest proportion of users and I see the 60px gap at the bottom of the screen something that can reasonably be addressed with some design decisions on a per project basis. You need to decide what you need.

2. Put an image in the holder and make it fill that holder

HTML

<div class="hero-holder">
  <img class="hero-image" src="images/hero-m.jpg">
</div>

CSS

.hero-image {
  display: none;
  /*
  We set this as the initial class for the image so that whilst the page is loading we don't show 
  an unstyled image in the hero-holder.
  */
}

.hero-image-width { 
  display: inline;
  width: 100%; 
  height: auto;
  position: relative; 
  top: 50%; 
}

.hero-image-height { 
  display: inline;
  height: 100%; 
  width: auto;
  position: relative; 
  left: 50%; 
}

JS

$(window).load(function() {    

  var theWindow         = $(window),
      bg                = $(".hero-image"),
      aspectRatio       = bg.width() / bg.height();
                    
  function resizeBg() {
    
    if ( (theWindow.width() / theWindow.height()) < aspectRatio ) {

        bg.removeClass().addClass('hero-image-height');

        var marginLeft = bg.width()/2;

        bg.css({"margin-top": "0", "margin-left": -marginLeft});

    } else {

        bg.removeClass().addClass('hero-image-width');

        var marginTop = bg.height()/2;

        bg.css({"margin-left": "0", "margin-top": -marginTop});

    }
          
  }
                          
  theWindow.resize(resizeBg).trigger("resize");

});

What I did here
This technique was essentially taken from the article mentioned in the beginning of this post. Initially that image within my full screen holder is hidden. Once the page and images are loaded jQuery inspects the ratio of the viewport and compares that to the ratio of our image. On that basis it applies a class to the images that displays it and gives it either a 100% height or a 100% width. This means the window is filled by a correctly proportioned image no matter what the size. The jQuery also centers the image.

This is tested and works on ALL browsers I could find.

3. Put a logo on top of the holder and center it vertically and horizontally

HTML

<div class="hero-holder">

  <img class="hero-image" src="images/hero-m.jpg">

  <div class="logo-holder">
      <div class="logo-centerer"></div>
      <img class="logo" src="images/logo.png" alt="logo"/>
  </div>

</div>

CSS

.logo-holder {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    
    text-align:center;
    font: 0/0 a;
}
 
.logo-centerer {
    display: inline-block;
    vertical-align: middle;
    height: 100%;
}
 
.logo {
    vertical-align: middle;
    display: inline-block;
    width: 400px;
    height: auto;
    max-width: 80%;
    max-height: 100%;
}

What I did here
I pulled this technique straight from this article. I positioned a logo holder within our hero holder using position:absolute so as to remove it from the flow of the document and put it on top of the background image. Then, I put a helpful div in that stretches to the height of the holder. Next, I put the logo in and vertically aligned it to the middle of the holder. This is a lovely little way to achieve an annoyingly tricky task across ALL browsers.

4. Add srcset for progressively serving the correct image size to each browser

HTML

<img 
  class="hero-image" 
  srcset="images/hero-s.jpg  800w, 
         images/hero-m.jpg  1024w,
         images/hero-l.jpg  2024w"
  sizes="(max-width: 1024px) 200vw,
         100vw"
  src="images/hero-m.jpg"
>

What I did here
So now I have essentially achieved what I set out to achieve but there is one problem – the image I need to use to cater for all screen sizes at all resolutions is massive. Ideally I don’t want to use this image for all screen sizes. To address this I have opted for using the very simple srcset syntax within my img tag. The way this works is I provide a set of images at different sizes, list them as I have above, tell the browser how much of the screen the image will take up and then the browser decides what image to use. Easy peasy. For browsers that do not support srcset yet (quite a few) I just serve the medium sized image and acknowledge that they will get a reasonably suitable image until they catch up.

Something to note here is that I have told the browser that for screens underneath 1024px the image will be 200% of the viewport width. This is because when a screen is portrait in orientation the size of the image will be roughly double the width of the screen. I have decided that most people with a viewport width of under 1024px will be using a device in portrait mode. You need to decide if you believe this to be true and adjust accordingly.

Read this article to get a better knowledge of the syntax.

Conclusion
So there we have it. A full-screen, centered background image with a centered logo for all browsers.

You can view the test page here and get the full source code here.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s