Posts Grid in Genesis using CSS Grid

CSS Grid is a fantastic new feature in CSS supported by all modern browsers (partial support in IE at the time of writing this) since March of this year.

Creating a grid is now as simple as declaring display property of the parent container element as grid.

The number of columns can be set in terms of fr, which stands for a fraction of the available space. For example, for a 3-column grid we set grid-template-columns: 1fr 1fr 1fr.

Want a 40px gap between the grid items? Simple. Just add grid-gap: 40px.

In this tutorial, we are going to

  • create a template part named `loop-archive.php` having the PHP to remove all hooks from the default Genesis loop actions, then add featured image, post title and entry meta in the loop, wrap all the posts in a div.articles so it can be declared as the grid.
  • load the above template part inside `home.php` (for the Posts page) and `archive.php` (for all other archives).
  • add the needed CSS including Flexbox fallback for non-supported browsers.

While the tutorial has been written for Genesis Sample, it should work with a few adjustments in any Genesis theme

Step 0 – Preparation

a) Let’s set 12 posts to be shown at Settings > Reading.

b) At Genesis > Theme Settings > Content Archives, include the featured image.

c) Create an empty Page titled say, Blog and set it as the Posts page at Settings > Reading. Do NOT apply Blog Page Template.

Step 1 – The template part

Create a file named say, loop-archive.php in the child theme directory having the following:

add_filter( 'body_class', 'custom_body_class' );
/**
 * Add `content-archive` class to the body element.
 *
 * @param  array $classes the current body classes
 * @return array $classes modified classes
 */
function custom_body_class( $classes ) {
    $classes[] = 'content-archive';

    return $classes;
}

// Force full width content.
add_filter( 'genesis_pre_get_option_site_layout', '__genesis_return_full_width_content' );

// Add opening div.articles tag before the latest post.
add_action( 'genesis_before_entry', function () {
    global $wp_query;

    if ( 0 === $wp_query->current_post && is_main_query() ) {
        echo '<div class="articles">';
    }
} );

// Remove all hooks from genesis_entry_header, genesis_entry_content and genesis_entry_footer actions.
$hooks = array(
    'genesis_entry_header',
    'genesis_entry_content',
    'genesis_entry_footer',
);

foreach( $hooks as $hook ) {
    remove_all_actions( $hook );
}

// Add featured image inside entry header.
add_action( 'genesis_entry_header', 'genesis_entry_header_markup_open' );
add_action( 'genesis_entry_header', 'genesis_do_post_image' );
add_action( 'genesis_entry_header', 'genesis_entry_header_markup_close' );

// Add entry title and entry meta in entry content.
add_action( 'genesis_entry_content', 'genesis_do_post_title' );
add_action( 'genesis_entry_content', 'genesis_post_meta' );

add_filter( 'genesis_post_meta', 'custom_post_meta_filter' );
/**
 * Customize entry meta.
 * @param  string $post_meta Existing entry meta
 * @return string            Modified entry meta
 */
function custom_post_meta_filter( $post_meta ) {
    $post_meta = '[post_categories before=""]';

    return $post_meta;
}

// Move .archive-pagination from under main.content to adjacent to it.
remove_action( 'genesis_after_endwhile', 'genesis_posts_nav' );
add_action( 'genesis_after_content', 'genesis_posts_nav' );

// Add closing div tag (for .articles) after the last post.
add_action( 'genesis_after_endwhile', function () {
    if ( is_main_query() ) {
        echo '</div>';
    }
} );

If you want content to be displayed, add

add_action( 'genesis_entry_content', 'genesis_do_post_content' );

below

add_action( 'genesis_entry_content', 'genesis_do_post_title' );

Step 2 – Load the template part

Create files named home.php and archive.php in the child theme directory having the following:

get_template_part( 'loop', 'archive' );

genesis();

Step 3 – CSS

Add the following in child theme’s style.css:

.content-archive .content {
    float: none;
}

.articles {
    display: -ms-grid;
    display: grid;

    -ms-grid-columns: 1fr 1fr 1fr;
    grid-gap: 40px;
    grid-template-columns: 1fr 1fr 1fr;
}

.content-archive .content .entry {
    margin-bottom: 0;
    padding: 0;
    border-radius: 3px;
    -webkit-box-shadow: 0 2px 4px 0 rgba(0,0,0,0.2);
    box-shadow: 0 2px 4px 0 rgba(0,0,0,0.2);
}

.content-archive .content .entry-content {
    padding: 20px;
}

.content-archive .content a.entry-image-link img {
    margin-bottom: 0;
    vertical-align: top;
}

.content-archive .content .entry-title {
    font-size: 18px;
}

.content-archive .content p.entry-meta {
    font-size: 14px;
    font-size: 1.4rem;
}

.content-archive .content .entry-meta a {
    color: #333;
    text-decoration: none;
}

.content-archive .content .entry-meta a:hover {
    color: #c3251d;
}

@media only screen and (max-width: 1023px) {
    .articles {
        -ms-grid-columns: 1fr 1fr;
        grid-template-columns: 1fr 1fr;
    }
}

@media only screen and (max-width: 500px) {
    .articles {
        -ms-grid-columns: 1fr;
        grid-template-columns: 1fr;
    }
}

A shortcut for grid-template-columns: 1fr 1fr 1fr is grid-template-columns: repeat(3, 1fr). So for a 4-column grid, you could use grid-template-columns: repeat(4, 1fr).

Step 4 – Flexbox fallback

We can use display: flex before display: grid as the supporting browsers will override the flex declaration with the grid. Then in a feature query (like media query, but used to test for supporting features) un-set the width/margin etc. values used for the Flexbox fallback in Grid supporting browsers.

In the CSS added earlier, change

.articles {
    display: -ms-grid;
    display: grid;

    -ms-grid-columns: 1fr 1fr 1fr;
    grid-gap: 40px;
    grid-template-columns: 1fr 1fr 1fr;
}

.content-archive .content .entry {
    margin-bottom: 0;
    padding: 0;
    border-radius: 3px;
    -webkit-box-shadow: 0 2px 4px 0 rgba(0,0,0,0.2);
    box-shadow: 0 2px 4px 0 rgba(0,0,0,0.2);
}

to

.articles {
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -ms-flex-wrap: wrap;
        flex-wrap: wrap;
    -webkit-box-pack: justify;
        -ms-flex-pack: justify;
            justify-content: space-between;

    display: -ms-grid;
    display: grid;

    -ms-grid-columns: 1fr 1fr 1fr;
    grid-gap: 40px;
    grid-template-columns: 1fr 1fr 1fr;
}

.content-archive .content .entry {
    margin-bottom: 0;
    padding: 0;
    border-radius: 3px;
    -webkit-box-shadow: 0 2px 4px 0 rgba(0,0,0,0.2);
    box-shadow: 0 2px 4px 0 rgba(0,0,0,0.2);

    width: 31.25%; /* http://d.pr/i/sVsJRM */
    margin-bottom: 3.125%;
}

.content-archive .content .entry:last-child {
    margin-right: auto;
    margin-left: 3.125%;
}

@supports ((display: -ms-grid) or (display: grid)) {
    .content-archive .content .entry {
        width: auto;
        margin-bottom: 0;
    }

    .content-archive .content .entry:last-child {
        margin-right: 0;
        margin-left: 0;
    }
}

and

@media only screen and (max-width: 1023px) {
    .articles {
        -ms-grid-columns: 1fr 1fr;
        grid-template-columns: 1fr 1fr;
    }
}

@media only screen and (max-width: 500px) {
    .articles {
        -ms-grid-columns: 1fr;
        grid-template-columns: 1fr;
    }
}

to

@media only screen and (max-width: 1023px) {
    .articles {
        -ms-grid-columns: 1fr 1fr;
        grid-template-columns: 1fr 1fr;
    }

    .content-archive .content .entry {
        width: 47.5%; /* http://d.pr/i/zHWeyP */
        margin-bottom: 5%;
    }

    .content-archive .content .entry:last-child {
        margin-left: 5%;
    }

    @supports ((display: -ms-grid) or (display: grid)) {
        .content-archive .content .entry {
            width: auto;
            margin-bottom: 0;
        }

        .content-archive .content .entry:last-child {
            margin-left: 0;
        }
    }
}

@media only screen and (max-width: 500px) {
    .articles {
        flex-direction: column;
        -ms-grid-columns: 1fr;
        grid-template-columns: 1fr;
    }

    .content-archive .content .entry {
        width: 100%; /* http://d.pr/i/zHWeyP */
    }

    .content-archive .content .entry:last-child {
        margin-left: 0;
    }
}

That’s it!

This site uses cookies to offer you a better browsing experience. By browsing this website, you agree to our use of cookies.