Responsive Featured Image function in WordPress themes

Responsive Featured Images in WordPress

[UPDATE - May 23rd '14]

I have included an update to this article which features new code and a downloadable plugin. Please note that this post simply shows a method I use to implement featured images into projects I am working on. If you attempt to use this method in your projects you could run into unforeseen issues. This is especially true if you are not familiar with developing in PHP or WordPress. I don’t recommend trying to implement this into your theme as such as it is not simply obtained by copy and paste. This article simply shows a method I use.

I really like including featured images when I am building WordPress themes. I almost feel like if they don’t have featured images for Posts and Pages it’s just not complete.

I also build all of the themes to work responsively. So if a user uploads a massive featured image this can be a problem. We don’t want to load a huge image in a mobile device. In the spirit of keeping our load times down I have been experimenting with a function that you can use in your themes that progressively loads the featured image using media queries.

First step is to make sure that your theme supports post thumbnails:

add_theme_support( 'post-thumbnails' );

Lets add some various sizes for our image.  You could add your own image sizes in this array using the add_image_size function. This depends on what type of image you want to display. For my purposes I choose a horizontal image with 6 sizes. These sizes are what are going to be loaded as our screen size gets larger and larger:

/**
 * Add Image Sizes in steps. These sizes are arbitrary
 */
if ( function_exists( 'add_image_size' ) ) {

    add_image_size( 'hero1', 300,  154 );
    add_image_size( 'hero2', 570,  293 );
    add_image_size( 'hero3', 800,  411 );
    add_image_size( 'hero4', 1060, 545 );
    add_image_size( 'hero5', 1200, 617 );
    add_image_size( 'hero6', 1370, 704 );
}

Next we’ll need a function to process the images and output them according to our screen size. So what we are going to do is grab all of those images in a function.

The following function is pretty simple. What is does is get the post thumbnail id using the post ID, retrieve the various image sizes associated with that image file and then display those images based on viewport size.

So we start by creating our function. This will go in our functions.php file underneath our image size declarations above:


function my_featured_image() {

	// call the global post variable
	global $post;

	// get the post thumbnail ID for the page or post
	$post_thumbnail_id = get_post_thumbnail_id( $post->ID );

} // end function my_featured_image

add_action( 'wp_head', 'my_featured_image' );

In the example above we get the featured image id using the get_post_thumbnail_id and the post variable. We store the featured image id in the $post_thumbnail_id variable. We are also adding the outputted CSS to our header which will render our styles using the action wp_head. The reason we are using wp_head is because it loads fast by printing the styles in our header. Since this will output the styles site wide on the front end we may want to wrap the function with a conditional statement. But we’ll get to that …

Next we will grab the image sizes we created earlier for the featured image.

	// store the image sizes in an array
	$img_sizes = array( 'hero1', 'hero2', 'hero3', 'hero4', 'hero5', 'hero6' );

Next we will grab the URL’s for each image size.

	// grab the URL for each image size and store in a variable
	foreach ( $img_sizes as $img_size ) {
		${ 'img_src_' . $img_size } = wp_get_attachment_image_src( $post_thumbnail_id, $img_size );
	}

Now we must do a little math to get a fluid aspect ratio for our images if we are going to be using CSS background images. I found an article by Rolf Timmermans which explains a method for obtaining the slope of our image in order to maintain our ratio. So lets get our slope by getting the width and height of our images. You can get the width and height by using the wp_get_attachment_image_src function from our previous function. Here is how we are going to get our slope:

    /**
     * Fluid aspect ratio
     *
     * http://voormedia.com/blog/2012/11/responsive-background-images-with-fixed-or-fluid-aspect-ratios
     *
     * The slope of the line corresponds to the padding-top attribute.
     * The start height corresponds to the min-height attribute.
     *
     * height(largest) - height(smallest) / width(largest) - width(smallest)
     */
    $slope = ( $img_hero6[2] - $img_hero1[2] ) / ( $img_hero6[1] - $img_hero1[1] );
    $padding_top = $slope * 100;

You’ll see that we took our largest photo and subtracted from our smallest for height and width. This produces our slope. We will use this number and multiply it by 100 to get our padding in a percentage which will help maintain our aspect ratio.

Next we need to add our styles. This will be the output of our function. We will be using CSS to call our image as a background-image. The trick is using the padding-top to maintain our aspect ratio.

Lets call our class we will apply to our element for use in our markup

    echo '<style type="text/css" media="screen" id="hero-img"> 

        .hero-img {
            width: 100%;
            padding-top:' . $padding_top   .'%;

            height:auto;
            position:relative;

            -webkit-background-size: cover;
            -moz-background-size: cover;
            background-size: cover;
            background-repeat: no-repeat;
            background-position: center center;
        }
    </style>';

Next is our magic media queries (ooooohhhhhh) which uses the width and height of our images to determine the breakpoints. I used the width of the image to determine the end of the breakpoint and the beginning of the next.  Min-width will serve as the width of the previous image size and max-width as the exact width of the image minus one pixel to allocated for the appropriate step. I will do this for each media query like so:

        @media screen and ( max-width: ' . ( $img_hero1[1] - 1 ) . 'px  ) {
            .hero-img {
                max-height: ' . $img_hero1[2] . 'px;
                background-image: url(' . esc_url( $img_hero1[0] ) . ');
            }
        }

        @media screen and ( min-width: ' . $img_hero1[1] . 'px ) and ( max-width: '. ( $img_hero2[1] - 1 ) . 'px ) {
            .hero-img {
                background-image: url(' . esc_url( $img_hero2[0] ) . ');
                min-height:' . $img_hero1[2] . 'px;
            }
        }

        @media screen and ( min-width: ' . $img_hero2[1] . 'px ) and ( max-width: '. ( $img_hero3[1] - 1 ) . 'px ) {
            .hero-img {
                background-image: url(' . esc_url( $img_hero3[0] ) . ');
                min-height:' . $img_hero2[2] . 'px;
            }
        }

        @media screen and ( min-width: ' . $img_hero3[1] . 'px ) and ( max-width: '. ( $img_hero4[1] - 1 ) . 'px ) {
            .hero-img {
                background-image: url(' . esc_url( $img_hero4[0] ) . ');
                min-height:' . $img_hero3[2] . 'px;
            }
        }

        @media screen and ( min-width: ' . $img_hero4[1] . 'px ) and ( max-width: '. ( $img_hero5[1] - 1 ) . 'px ) {
            .hero-img {
                background-image: url(' . esc_url( $img_hero5[0] ) . ');
                min-height:' . $img_hero4[2] . 'px;
            }
        }

        @media screen and ( min-width: ' . $img_hero5[1] . 'px ) {
            .hero-img {
                background-image: url(' . esc_url( $img_hero6[0] ) . ');
                min-height:' . $img_hero5[2] . 'px;
            }
        }

The max-height on the first media query ensures a minimum height for the first image. And the min-height for the .hero-img in each media query is the corresponding height of the element in the previous media query.

So now lets put all this in a function. We must place a conditional within the function to output the code only if the post or page has a featured image so lets wrap our function in a with an if statement. To do this we will use the has_post_thumbnail function.

/**
 * Responsive Featured Image Background
 *
 * @link http://s2webpress.com//responsive-featured-image-function-in-wordpress-themes/
 * @author Steven Slack
 *
 */
function s2_responsive_featured_image() {

    // call the global post variable
    global $post;

    if ( has_post_thumbnail( $post->ID ) ) : // checks whether the post has the featured image set

    // get the post thumbnail ID for the page or post
    $post_thumbnail_id = get_post_thumbnail_id( $post->ID );

    // store the image sizes in an array. You can also add your own image sizes with the add_image_size function
    $img_sizes = array( 'hero1', 'hero2', 'hero3', 'hero4', 'hero5', 'hero6' );

    /**
     * grab the URL for each image size and store in a variable
     * http://codex.wordpress.org/Function_Reference/wp_get_attachment_image_src
     */
    foreach ( $img_sizes as $img_size ) {
        ${ 'img_' . $img_size } = wp_get_attachment_image_src( $post_thumbnail_id, $img_size );
    }

    /**
     * Fluid aspect ratio
     *
     * http://voormedia.com/blog/2012/11/responsive-background-images-with-fixed-or-fluid-aspect-ratios
     *
     * The slope of the line corresponds to the padding-top attribute.
     * The start height corresponds to the min-height attribute.
     *
     * height(largest) - height(smallest) / width(largest) - width(smallest)
     */
    $slope = ( $img_hero6[2] - $img_hero1[2] ) / ( $img_hero6[1] - $img_hero1[1] );
    $padding_top = $slope * 100;

    echo '<style type="text/css" media="screen" id="hero-img"> 

        .hero-img {
            width: 100%;
            padding-top:' . $padding_top   .'%;

            height:auto;
            position:relative;

            -webkit-background-size: cover;
            -moz-background-size: cover;
            background-size: cover;
            background-repeat: no-repeat;
            background-position: center center;
        }

        @media screen and ( max-width: ' . ( $img_hero1[1] - 1 ) . 'px  ) {
            .hero-img {
                max-height: ' . $img_hero1[2] . 'px;
                background-image: url(' . esc_url( $img_hero1[0] ) . ');
            }
        }

        @media screen and ( min-width: ' . $img_hero1[1] . 'px ) and ( max-width: '. ( $img_hero2[1] - 1 ) . 'px ) {
            .hero-img {
                background-image: url(' . esc_url( $img_hero2[0] ) . ');
                min-height:' . $img_hero1[2] . 'px;
            }
        }

        @media screen and ( min-width: ' . $img_hero2[1] . 'px ) and ( max-width: '. ( $img_hero3[1] - 1 ) . 'px ) {
            .hero-img {
                background-image: url(' . esc_url( $img_hero3[0] ) . ');
                min-height:' . $img_hero2[2] . 'px;
            }
        }

        @media screen and ( min-width: ' . $img_hero3[1] . 'px ) and ( max-width: '. ( $img_hero4[1] - 1 ) . 'px ) {
            .hero-img {
                background-image: url(' . esc_url( $img_hero4[0] ) . ');
                min-height:' . $img_hero3[2] . 'px;
            }
        }

        @media screen and ( min-width: ' . $img_hero4[1] . 'px ) and ( max-width: '. ( $img_hero5[1] - 1 ) . 'px ) {
            .hero-img {
                background-image: url(' . esc_url( $img_hero5[0] ) . ');
                min-height:' . $img_hero4[2] . 'px;
            }
        }

        @media screen and ( min-width: ' . $img_hero5[1] . 'px ) {
            .hero-img {
                background-image: url(' . esc_url( $img_hero6[0] ) . ');
                min-height:' . $img_hero5[2] . 'px;
            }
        }

        .lt-ie9 .hero-img {
            background-image: url(' . esc_url( $img_hero5[0] ) . ');
            min-height:' . $img_hero5[2] . 'px;
        }    

    </style>';

   endif; // end if the featured image is set

} // end function s2_responsive_featured_image

add_action( 'wp_head', 's2_responsive_featured_image' );

Now  lets create a smaller function which will output the code in our templates.

/**
 * Return the featured image
 * @return html string
 */
function the_responsive_featured_image() {

    global $post;

    if( has_post_thumbnail( $post->ID ) ) :

	    // The Posts featured image title attribute
	    $title_attr = get_post( get_post_thumbnail_id() )->post_title; //The Post Title

		$featured_img = '<div class="hero-img" role="img" aria-label="' . $title_attr . '"></div>';

		return $featured_img;

	endif;    

}

Here again we are using the has_post_thumbnail function to check whether the current post has a featured image. If no image is set then the markup will not be returned.

Now you can place the function inside your theme where you would like the image to show up.

IMPORTANT!!!! – this does not work in the loop! This function is meant to be used for single posts and pages.

<?php
    if( function_exists( 'the_responsive_featured_image' ) ) {
         echo the_responsive_featured_image();
    }
?>

This could be used in many different ways as well. I like to use them for big featured images at the tops of pages. It could even be used as a main image on your front page. You can also add in your custom styles markup as well such as overlaying the title on top of the image.

Remember that you may want to check your image sizes in Settings->Media or add your own sizes to the $img_sizes array.

You can download this code here and use it in your themes. Remember to regenerate your thumbnails after installing and activating this plugin!

Download the responsive featured image plugin

29 thoughts on “Responsive Featured Image function in WordPress themes

  1. Thanks for posting this! It was exactly what I was trying to do, but I don’t quite have the PHP chops to code it all myself yet.

  2. I found this post very helpful Steven and it put me on the right path, but I am having some trouble with delivering the actual image. The media queries and breakpoints are working, but the source URL is nowhere to be found in the CSS background-image style.

        • Do you have a featured image set for that post or page? I think that is your issue. I have updated the article with a more appropriate conditional checking whether the post or page has a featured image set. I have replaced line 13 where it reads:
          if ( is_home() ) :
          with this:
          if ( has_post_thumbnail( $post->ID ) ) :

          Check out the updated code above.

          • Yes, I have a featured image set for that post. I understand the updates as well, I modified those lines earlier actually. :) Thank you. Now with the new “the_featured_image_output” function I can’t get the image to appear at all, let alone the source URL. I am simply trying to apply the featured image to several custom queries i.e.

            $args = array(
            ‘cat’ => ‘-10′,
            ‘post_type’ => ‘casestudy’,
            ‘posts_per_page’ => 1
            );
            $query = new WP_Query( $args );

            I want to eliminate the thumbnail function for your responsive solution. I appreciate the help.

          • The custom query shouldn’t matter. The function the_featured_image_output() is made to only output the code if the featured image is set. This may be why you are not seeing anything now. But, since you said you had the featured image set I am not really sure what is happening. If you fill out my contact form and provide some FTP credentials I’d be happy to take a look.

  3. Hello there,
    I’m new to this but I followed the instructions and I would like to include the
    “” within a loop to show the thumbnails of each post.
    Is there anything i need to change in functions.php?
    Regards

    • You can just include the snippet < ?php echo the_featured_image_output(); ?> in the loop and as long as the the_featured_image_output function is in your functions.php file along the the my_featured_image function everything should work. :)

      • Hey steven thanks for the reply,
        I’ve included everything in the functions.php and placed inside the loop of . But for some odd reason all the posts are appearing with the same thumbnails. (correct links to the posts just the thumbnails are the same)
        Is there something I’m missing? Maybe clashing with something?
        Regards and thanks again for the response

        • Hey steven thanks for the reply,
          I’ve included everything in the functions.php and placed: php echo the_featured_image_output() inside the loop of: ? php while (have_posts()) : the_post(); ? >. But for some reason all the posts are appearing with the same thumbnails. (correct links to the posts just the thumbnails are the same)
          Is there something I’m missing? Maybe clashing with something?
          Regards and thanks again for the response

          • I created this for use on single post pages. I haven’t written a function for the blog loop. But it seems that if it was used in the same way it would output too many styles in the header. I recommend just using it for your single posts and pages.

  4. Hey steven thanks for the reply,
    I’ve included everything in the functions.php and placed: php echo the_featured_image_output() inside the loop of: ? php while (have_posts()) : the_post(); ? >. But for some reason all the posts are appearing with the same thumbnails. (correct links to the posts just the thumbnails are the same)
    Is there something I’m missing? Maybe clashing with something?
    Regards and thanks again for the response

  5. You saved my life, dude! I just needed some quick help to finish my job before the end of the day and thanks to your post it took me only a few minutes to do it instead of a lethal work! Thank you!

  6. Hi,
    This looks fine work. I was actually searching for ways to add links to WP featured images but given the theme I’m using is meant to have fluid images this looks worth reading and trying to implement! Many thanks :-)

  7. Just to see I took out all the old code and replaced it with the new code (without customizing anything) and it’s at least closer to full size, but still cropping. Also it’s only showing up at all when the browser is over 1200 in width. When I resize it disappears. Sorry, I swear I’m not trying to take up too much of your time. I’m just really confused.

      • Ok. Before even worrying about vertical vs horizontal ratios…

        I’m getting a “division by zero” error back when I use the new code. Am I supposed to input anything into that part of the script? I’d assume it’s getting all the values it needs automatically.

        It seems like it’s reading

        ( $img_hero6[2] – $img_hero1[2] ) / ( $img_hero6[1] – $img_hero1[1] )

        as

        ( $img_hero6 – $img_hero1 ) / ( $img_hero6 – $img_hero1 )

        for some reason rather than inputting variables in between the brackets.

        (Thanks for your support and patience)

        • You’ll notice that the image sizes in the array correspond here with these variables. For example I have an image size named ‘hero6′, the variable $img_hero6 contains all of the image data associated with that image size. This is because we are storing the data array from wp_get_attachment_image_src. You have to include the brackets to reference the array data. Refer to the documentation here. Just make sure to rename your variables according to your image sizes. Hope that helps.

          • Not at all, unfortunately. No matter what I do the code doesn’t work. I’m afraid I’m just going to have to start over and try a new solution as I’ve been hacking at this for 6 hours straight and am no closer than I was when I started. :(

Comments are closed.