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!