This is a guest post by Josh Pollock

The Pinterest-like grid display of posts has been a popular design for WordPress blog index pages for a while. It is popular not only because it mimics the look of the popular social media site, but also because it makes the best use of space on the screen. On a WordPress blog index, it allows each post preview to be the size it naturally needs to be, without leaving extra space.

In this tutorial, I will show you how to use the popular Masonry JavaScript Library to create cascading grid layouts for your blog index, as well as archive pages for your theme. I will address a few issues that you need to consider for mobile-optimization and how to solve them.

Note: This is an advanced level tutorial for those who feel comfortable editing WordPress themes and have sufficient HTML/CSS knowledge.

Step 1: Add Necessary Libraries To Your Theme

Update: WordPress 3.9 now includes the latest version of Masonry.

First you need to load Masonry into your theme, using this code:

if (! function_exists('slug_scripts_masonry') ) : if ( ! is_admin() ) : function slug_scripts_masonry() { wp_enqueue_script('masonry'); wp_enqueue_style('masonry’, get_template_directory_uri().'/css/’); } add_action( 'wp_enqueue_scripts', 'slug_scripts_masonry' ); endif; //! is_admin() endif; //! slug_scripts_masonry exists

This code simply loads masonry and makes it available to your theme’s template files (see our guide on how to properly add JavaScripts and Styles in WordPress). Also note that we are not adding jQuery as a dependency for either. One of the advantages of Masonry 3 is that it does not require jQuery, but can be used with it. In my experience, initializing Masonry without jQuery is more reliable, and opens up the possibility of skipping loading jQuery, which can help with both page load times and compatibility issues.

Step 2: Initialize The Javascript

This next function sets up Masonry, defines the containers that will be used with it and also makes sure everything happens in the right order. Masonry needs to do calculate the size of each of the items on the page in order to layout its grid dynamically. A problem I’ve run into with Masonry in many browsers is that Masonry will miscalculate the height of items with slow loading images, leading to overlapping items. The solution is to use imagesLoaded to prevent Masonry from calculating the layout until all images are loaded. This ensures proper sizing.

This is the function and action that will output the initialization script in the page footer:

if ( ! function_exists( 'slug_masonry_init' )) : function slug_masonry_init() { ?> <script> //set the container that Masonry will be inside of in a var var container = document.querySelector('#masonry-loop'); //create empty var msnry var msnry; // initialize Masonry after all images have loaded imagesLoaded( container, function() { msnry = new Masonry( container, { itemSelector: '.masonry-entry' }); }); </script> <?php } //add to wp_footer add_action( 'wp_footer', 'slug_masonry_init' ); endif; // ! slug_masonry_init exists

The function is explained step by step with inline comments. What the Javascript function does is tell Masonry to look inside a container with the id “masonry-loop” for items with the class “masonry-entry” and to calculate the grid only after images are loaded. We set the outer container with querySelector and the inner with itemSelector.

Step 2: Create Masonry Loop

Instead of adding the HTML markup for Masonry directly to a template we are going to create a separate template part for it. Create a new file called “content-masonry.php” and add it to your theme. This will allow you to add the Masonry loop to as many different templates as you want.

In your new file you will add the code shown below. The markup is similar to what you would normally see for any content preview. You can modify it anyway you need to, just be sure that the outermost element has the class of “masonry-entry” that we set as the itemSelector in the last step.

<article class="masonry-entry" id="post-<?php the_ID(); ?>" <?php post_class(); ?> > <?php if ( has_post_thumbnail() ) : ?> <div class="masonry-thumbnail"> <a href="<?php the_permalink(' ') ?>" title="<?php the_title(); ?>"><?php the_post_thumbnail('masonry-thumb'); ?></a> </div><!--.masonry-thumbnail--> <?php endif; ?> <div class="masonry-details"> <h5><a href="<?php the_permalink(' ') ?>" title="<?php the_title(); ?>"><span class="masonry-post-title"> <?php the_title(); ?></span></a></h5> <div class="masonry-post-excerpt"> <?php the_excerpt(); ?> </div><!--.masonry-post-excerpt--> </div><!--/.masonry-entry-details --> </article><!--/.masonry-entry-->

This markup has classes for each of its parts so you can add markup to match your theme. I like adding a nice, slightly rounded border to .masonry-entry. Another nice option is no border for .masonry-entry, but to give it a slight shadow. That looks particularly good when the post thumbnail extends the whole way to the border of the container, which can be accomplished by giving .masonry-thumbnail margins and paddings of 0 in all directions. You will want to add all of these styles in a file called masonry.css in your theme’s css directory.

Step 3: Add Masonry Loop To Templates

Now that we have our template part you can use it in any template in your theme you like. You might add it to index.php, but not category.php if you don’t want it used for category archives. If you only want it used on your theme’s home page, when its set to show blog posts, you would use it in home.php. Wherever you choose all you need to do is wrap your loop in a container with the id “masonry-loop” and add the template part into the loop using get_template_part(). Be sure to start the masonry loop container before while (have_posts() ).

For example, here is the main loop from twentythirteen’s index.php:

<?php if ( have_posts() ) : ?> <?php /* The loop */ ?> <?php while ( have_posts() ) : the_post(); ?> <?php get_template_part( 'content', get_post_format() ); ?> <?php endwhile; ?> <?php twentythirteen_paging_nav(); ?> <?php else : ?> <?php get_template_part( 'content', 'none' ); ?> <?php endif; ?>

And here it is modified to get use our Masonry loop:

<?php if ( have_posts() ) : ?> <div id="masonry-loop"> <?php /* The loop */ ?> <?php while ( have_posts() ) : the_post(); ?> <?php get_template_part( 'content', 'masonry' ?> <?php endwhile; ?> </div><!--/#masonry-loop--> <?php twentythirteen_paging_nav(); ?> <?php else : ?> <?php get_template_part( 'content', 'none' ); ?> <?php endif; ?>

Step 4: Set Responsive Widths of Masonry Items

There are several ways that you can set the width of each Masonry item. You can set the width using a number of pixels when you initialize Masonry. I’m not a fan of doing that since I use responsive themes and it requires some complex media queries to get things right on small screens. For responsive designs, I’ve found the best thing to do is set a width value for .masonry-entry with a percentage, based on how many items you want in a row and let Masonry do the rest of the math for you.

All this requires is dividing the 100 by the number of items you want to set the percentage in a simple entry in your theme’s style.css. For example, if you want four items in each row you could do this in your masonry.css file:

.masonry-entry{width:25%}

Step 5: Mobile Optimization

We could stop here, but I don’t think the end-result works incredibly well on small phone screens. Once you’re happy with how your theme looks with the new Masonry grid on your desktop, check it out on your phone. If you’re not happy about how it looks on your phone, then you will need to do a little work.

I don’t think there is enough room on a phone’s screen for everything we added to our content-masonry template part. Two good solutions available to you are to shorten the excerpt for phones or skip it entirely. Here is an extra function you can add to your theme’s functions.php to do that. Because I don’t think these issues are a problem on tablets, I am using a great plugin Mobble in all of the examples in this section to only make the changes on phones, not tablets. I am also checking to see if Mobble is active before using it and if necessary falling back to the more general mobile detection function wp_is_mobile which is built into WordPress.

if (! function_exists('slug_custom_excerpt_length') ) : function slug_custom_excerpt_length( $length ) { //set the shorter length once $short = 10; //set long length once $long = 55; //if we can only set short excerpt for phones, else short for all mobile devices if (function_exists( 'is_phone') { if ( is_phone() ) { return $short; } else { return $long; } } else { if ( wp_is_mobile() ) { return $short; } else { return $long; } } } add_filter( 'excerpt_length', 'slug_custom_excerpt_length', 999 ); endif; // ! slug_custom_excerpt_length exists

As you can see we start by storing the long excerpt length and short excerpt length in variables, since we will be using those values twice and we want to be able to change them from one place if we need to later on. From there we test if we can use Mobble’s is_phone(). If so we set the short excerpt for phones and the long excerpt if we are not. After that we do the same basic thing, but using wp_is_mobile, which will affect all mobile devices ,if is_phone() can’t be used. Hopefully the else part of this function will never be used, but it’s good to have a backup just in case. Once the excerpt length logic is set it just comes down to hooking the function to the excerpt_length filter.

Shortening the excerpt is one option, but we can also do away with it entirely with a simple process. Here is a new version of content-masonry, with the entire excerpt portion ommited on phones:

<article class="masonry-entry" id="post-<?php the_ID(); ?>" <?php post_class(); ?> > <?php if ( has_post_thumbnail() ) : ?> <div class="masonry-thumbnail"> <a href="<?php the_permalink(' ') ?>" title="<?php the_title(); ?>"><?php the_post_thumbnail('masonry-thumb'); ?></a> </div><!--.masonry-thumbnail--> <?php endif; ?> <div class="masonry-details"> <h5><a href="<?php the_permalink(' ') ?>" title="<?php the_title(); ?>"><span class="masonry-post-title"> <?php the_title(); ?></span></a></h5> <?php //put the excerpt markup in variable so we don't have to repeat it multiple times. $excerpt = '<div class="masonry-post-excerpt">'; $excerpt .= the_excerpt(); $excerpt .= '</div><!--.masonry-post-excerpt-->'; //if we can only skip for phones, else skip for all mobile devices if (function_exists( 'is_phone') { if ( ! is_phone() ) { echo $excerpt; } } else { if ( ! wp_is_mobile() ) { echo $excerpt; } } ?> </div><!--/.masonry-entry-details --> </article><!--/.masonry-entry-->

This time we have are testing to see if we are not on a phone/ mobile device and if so we return the excerpt part of our loop. If we are on a phone/ mobile device we do nothing.

Another thing you might want to do is increase the width of the Masonry items, which reduces the number in a row, on mobile devices. In order to do this, we will add a different inline style to the header based on device detection. Since this function uses wp_add_inline_styles it will be dependent on a specific style sheet. In this case I’m using masonry.css, which you might want, for keeping your masonry styles separate. If you don’t end up using that you can use the handle from another, already registered stylesheet.

if ( ! function_exists ( 'slug_masonry_styles' ) ) : function slug_masonry_styles() { //set the wide width $wide = '25%'; //set narrow width $narrow = '50%'; /**Determine value for $width**/ //if we can only set narrow for phones, else narrow for all mobile devices if (function_exists( 'is_phone') { if ( is_phone() ) { $width = $narrow; } else { $width = $wide; } } else { if ( wp_is_mobile() ) { $width = $narrow; } else { $width = $wide; } } /**Output CSS for .masonry-entry with proper width**/ $custom_css = ".masonry-entry{width: {$width};}"; //You must use the handle of an already enqueued stylesheet here. wp_add_inline_style( 'masonry', $custom_css ); } add_action( 'wp_enqueue_scripts', 'slug_masonry_styles' ); endif; // ! slug_masonry_styles exists

This function eneuques the custom stylesheet, and then sets a value for width using what should now be very familiar logic. After that we create the $custom_css variable by passing the value for width into an otherwise regular looking bit of CSS with {$width}. After that we use wp_add_inline_style to tell WordPress to print our inline styles in the header whenever the Masonry stylesheet is being used and then we hook the whole function to wp_enqueue_scripts and we are done.

If you chose to combine your Masonry styles into an existing stylesheet, be sure to use the handle of that style sheet with wp_add_inline_style or your inline styles will not be included. I like to use wp_add_inline_style because I generally wrap the action hook for enqueueing Masonry in a conditional so it only gets added when needed. For example if I am only using Masonry on my blog index and archive pages I would do this:

if ( is_home() || is_archive() ) { add_action( 'wp_enqueue_scripts', 'slug_scripts_masonry' ); }

These last few examples should open up some other ideas in your brain. For example, you could use similar logic to skip Masonry altogether on a mobile device. Also wp_add_inline_style() is a lesser used, yet very helpful function as it allows you to programmatically set different styles based on any type of conditional. It can allow you to radically change the style of any element based on not only device detection, but the changes could also be based on which template is being used, or even if the user is logged in or not.

I hope you see these different changes I am making as an opportunity to get creative. Masonry and similar cascading grid systems have been popular for awhile now, so its time for some new twists on this popular idea. Show us in the comments what cool ways you’ve come up with for using Masonry in a WordPress theme.

A multi-purpose WordPress guy, Josh Pollock writes about WordPress, does theme development, serves as the community manager for the Pods Framework and advocates open source solutions for sustainable design.