34SP.com Blog

WordPress Materialized: Creating a Material Design Theme, pt. 3 – Sidebars and Polish

So, we’ve integrated Materialize.css and setup our basic structure. In the final part of this WordPress web hosting tutorial, let’s style our sidebars and comments, and add some extra theme polish.

To begin with, add the card styles to your primary sidebar. Open your functions.php, and around line 86, you should see the widget init function. We’re going to wrap the aside element in the card class, so find this:

		'before_widget' => '<aside id="%1$s" class="widget %2$s">',
		'after_widget'  => '</aside>',

…and change it to this:

		'before_widget' => '<div class="card"><aside id="%1$s" class="widget %2$s">',
		'after_widget'  => '</aside></div>',

We need to add some padding to the content and style our widget titles, so in style.css, add the following:

.textwidget {
  padding: 10px 20px;
}

#secondary h1.widget-title {
  font-size: 2.6rem;
  font-weight: 300;
  display: block;
  padding: 10px 20px;
  margin: 0 0 20px;
}

We’re adding a default style for textwidget for now. The other common widget elements will have their own styles.

The widget title is an element we’ll provide color options for, so in your blue.css, add the following:

#secondary h1.widget-title {
  background: #2196f3;
  color: #fff;
}

View it in your browser, and we now have card style widgets with a color coordinated title bar:

Materialize.css has an element called “Avatar Collections” which we can use to dress up our recent posts display. To use it, we’re going to over ride the default recent posts behavior by extending the WP_Widget_Recent_Posts class. In your functions.php, add the following:

Class My_Recent_Posts_Widget extends WP_Widget_Recent_Posts {
 
	function widget($args, $instance) {
	
		extract( $args );
		
		$title = apply_filters('widget_title', empty($instance['title']) ? __('Recent Posts') : $instance['title'], $instance, $this->id_base);
				
		if( empty( $instance['number'] ) || ! $number = absint( $instance['number'] ) )
			$number = 10;
					
		$r = new WP_Query( apply_filters( 'widget_posts_args', array( 'posts_per_page' => $number, 'no_found_rows' => true, 'post_status' => 'publish', 'ignore_sticky_posts' => true ) ) );
		if( $r->have_posts() ) :
			
			echo $before_widget;
			if( $title ) echo $before_title . $title . $after_title; ?>
			<ul class="collection rpwidget">
				<?php while( $r->have_posts() ) : $r->the_post(); ?>				
				<li class="collection-item avatar">
					<?php echo get_the_post_thumbnail( $post_id, 'thumbnail', array( 'class' => 'alignleft circle' ) ); ?>
					<span class="title"><a href="<?php the_permalink(); ?>" title="<?php the_title(); ?>"><?php the_title(); ?></a></span>
					<p>by <?php the_author(); ?> on <?php echo get_the_date(); ?></p>
				</li>
				<?php endwhile; ?>
			</ul>
			 
			<?php
			echo $after_widget;
		
		wp_reset_postdata();
		
		endif;
	}
}
function my_recent_widget_registration() {
  unregister_widget('WP_Widget_Recent_Posts');
  register_widget('My_Recent_Posts_Widget');
}
add_action('widgets_init', 'my_recent_widget_registration');

The first part sets up our recent posts widget to use the collections classes in our display. The registration function unregisters the default display, and replaces it with our own. We’ll need to add some supporting styles to make sure the collection element fits our sidebar, so open up style.css and add the following:

.rpwidget {
	border: 0;
}

.rpwidget .collection-item.avatar {
height: 100%;
padding-left: 72px;
position: relative;
}

.rpwidget .collection-item {
background-color: #fff;
line-height: 1.5rem;
padding: 20px 20px;
margin: 0px;
border-bottom: 1px solid #e0e0e0;
}

ul.collection.rpwidget {
  background: #fff;
  padding: 0;
  margin-top: -20px;
}

Now our recent posts display should look something like this:

We’re going to do something similar with the custom menu widgets, but since it’s more work to write a custom nav-walker to apply styles to the individual list items, we’re going to fake it with CSS. In your style.css, add the following:

aside.widget_nav_menu {
  margin-bottom: 0;
}

.widget ul.menu {
  margin: -20px 0 0;
  padding: 0;
}

.widget ul.menu li {
  width: 100%;
  border-bottom: 1px solid #eee;
  padding: 10px;
}

.widget ul.menu li:last-of-type {
  border-bottom: 0;
}

With this addition, most of the standard WordPress widgets should display nicely, but you’ll want to test a variety of widgets for custom styling needs.

Clicking into any of our blog posts, we see that posts and pages still need some layout help. We’ll need to apply our grids to the inner pages. Open single.php, page.php, and archive.php. We need to wrap the main content row in a “row” class div, and apply our grid classes to the #primary div. Your end result on all three files should look something like this (single.php shown here):

get_header(); ?>
	<div class="row">
	<div id="primary" class="content-area col s12 m7 l8">
		<main id="main" class="site-main" role="main">

		<?php while ( have_posts() ) : the_post(); ?>

			<?php get_template_part( 'content', 'single' ); ?>

			<?php the_post_navigation(); ?>

			<?php
				// If comments are open or we have at least one comment, load up the comment template
				if ( comments_open() || get_comments_number() ) :
					comments_template();
				endif;
			?>

		<?php endwhile; // end of the loop. ?>

		</main><!-- #main -->
	</div><!-- #primary -->

<?php get_sidebar(); ?>
</div>
<?php get_footer(); ?>

You’ll also want to copy the contents of content.php to content-single.php, to wrap the single blog posts in the same image card UI as the home page.

The single post view still need some polish, so we’re going to dress up the post navigation, and add our own custom Material Design comments section. First, open your functions.php and add the following:

// Dress up the post navigation

add_filter( 'next_post_link' , 'my_nav_next' , 10, 4);
add_filter( 'previous_post_link' , 'my_nav_previous' , 10, 4);
 
function my_nav_next($output, $format, $link, $post ) {
 $text = ' previous post';
 $rel = 'prev';

 return sprintf('<a href="%1$s" rel="%3$s" rel="nofollow" class="waves-effect waves-light btn left"><span class="white-text"><i class="mdi-navigation-chevron-left left"></i>%2$s</span></a>' , get_permalink( $post ), $text, $rel );
}

function my_nav_previous($output, $format, $link, $post ) {
 $text = ' next post';
 $rel = 'next';
 
 return sprintf('<a href="%1$s" rel="%3$s" rel="nofollow" class="waves-effect waves-light btn right"><span class="white-text">%2$s<i class="mdi-navigation-chevron-right right"></i></span></a>' , get_permalink( $post ), $text, $rel );
}

This will apply the Material classes to our previous and next buttons. We’ll need some additional styling to get it matched up with our theme, so open style.css, and add the following:

nav.navigation.post-navigation {
  background: transparent;
  box-shadow: 0 0;
}

Then, in our blue.css, add the colors for our buttons:

.nav-links .btn {
  background: #2196f3;
}

Finally, let’s dress up the comments section. First, let’s restructure it to use Material classes. In your functions.php, add the following:

(Note: I’m using ‘materialized’ as the namespace for this theme, so replace that everywhere you see it with your theme’s namespace).

// Custom comment functionality

add_filter('get_avatar','change_avatar_css');

function change_avatar_css($class) {
$class = str_replace("class='avatar", "class='avatar circle left z-depth-1", $class) ;
return $class;
}

add_filter('comment_reply_link', 'materialized_reply_link_class');


function materialized_reply_link_class($class){
    $class = str_replace("class='comment-reply-link", "class='waves-effect waves-light btn", $class);
    return $class;
}

function materialized_comment($comment, $args, $depth) {
	$GLOBALS['comment'] = $comment;
	extract($args, EXTR_SKIP);

	if ( 'div' == $args['style'] ) {
		$tag = 'div';
		$add_below = 'comment';
	} else {
		$tag = 'li';
		$add_below = 'div-comment';
	}
?>
	<<?php echo $tag ?> <?php comment_class( empty( $args['has_children'] ) ? '' : 'parent' ) ?> id="comment-<?php comment_ID() ?>">
	<?php if ( 'div' != $args['style'] ) : ?>
	<div id="div-comment-<?php comment_ID() ?>" class="comment-body">
	<?php endif; ?>
	<div class="comment-author vcard">
	<?php if ( $args['avatar_size'] != 0 ) echo get_avatar( $comment, $args['avatar_size'] ); ?>
	<div class="comment-meta commentmetadata"><a href="<?php echo htmlspecialchars( get_comment_link( $comment->comment_ID ) ); ?>">
		<?php
				/* translators: 1: date, 2: time */
				printf( __('%1$s at %2$s'), get_comment_date(),  get_comment_time() ); ?></a><?php edit_comment_link( __( '(Edit)' ), '  ', '' );
		?>
	</div>
	<?php printf( __( '<cite class="fn">%s</cite> <span class="says">wrote:</span>' ), get_comment_author_link() ); ?>
	</div>
	<?php if ( $comment->comment_approved == '0' ) : ?>
		<em class="comment-awaiting-moderation"><?php _e( 'Your comment is awaiting moderation.' ); ?></em>
		<br />
	<?php endif; ?>


	<?php comment_text(); ?>

	<div class="reply right">
	<?php comment_reply_link( array_merge( $args, array( 'add_below' => $add_below, 'depth' => $depth, 'max_depth' => $args['max_depth'] ) ) ); ?>
	</div>
	<?php if ( 'div' != $args['style'] ) : ?>
	<div class="clear"></div>
	</div>
	<?php endif; ?>
	<div class="divider"></div>
<?php
}

The change_avatar_css function applies the round style and a box-shadow to the commenter’s avatar. The materialized_reply_link function adds Material classes to the reply links. The materialized_comment function structures the general comment layout to use the modified avatar and reply buttons. Now for the heavy part: to wrap all of our individual comment elements in Material classes, we’re going to have to rewrite the comments.php file entirely.

Replace the contents of your comments.php with the following:

(Note: I’m using ‘materialized’ as the namespace for this theme, so replace that everywhere you see it with your theme’s namespace).

<?php
/**
 * The template for displaying comments.
 *
 * The area of the page that contains both current comments
 * and the comment form.
 *
 * @package materialized
 */

/*
 * If the current post is protected by a password and
 * the visitor has not yet entered the password we will
 * return early without loading the comments.
 */
if ( post_password_required() ) {
	return;
}

// Edit the default comment structure

$commentargs = array(
  'id_form'           => 'commentform',
  'id_submit'         => 'submit',
  'class_submit'      => 'submit',
  'name_submit'       => 'submit',
  'title_reply'       => __( 'Comment on this:' ),
  'title_reply_to'    => __( 'Reply to %s' ),
  'cancel_reply_link' => __( 'Cancel Reply' ),
  'label_submit'      => __( 'Post Comment' ),
  'format'            => 'xhtml',
  'fields'						=>
  	apply_filters( 'comment_form_default_fields', array(
  		'author' => '<div class="input-field col s12">' . '<i class="mdi-action-account-circle prefix"></i><input id="author" name="author" type="text" value="' . esc_attr( $commenter['comment_author'] ) . '" size="30" /><label for="author">' . __( 'Name' ) . '</label></div>',  
			'email'  => '<div class="input-field col s12">' . '<i class="mdi-communication-email prefix"></i><input id="email" name="email" type="text" value="' . esc_attr(  $commenter['comment_author_email'] ) . '" size="30" /><label for="email">' . __( 'Email' ) . '</label>'.'</div>',
			'url'		 => ''
		)),
  'comment_field' =>  '<div class="input-field col s12"><i class="mdi-editor-mode-edit prefix"></i><textarea id="comment" name="comment" class="materialize-textarea">' . '</textarea><label for="comment">' . _x( 'Comment', 'noun' ) . '</label></div>',
  'must_log_in' => '<p class="must-log-in">' .
    sprintf(
      __( 'You must be <a href="%s">logged in</a> to post a comment.' ),
      wp_login_url( apply_filters( 'the_permalink', get_permalink() ) )
    ) . '</p>',

  'logged_in_as' => '<p class="logged-in-as">' .
    sprintf(
    __( 'Logged in as <a href="%1$s">%2$s</a>. <a href="%3$s" title="Log out of this account">Log out?</a>' ),
      admin_url( 'profile.php' ),
      $user_identity,
      wp_logout_url( apply_filters( 'the_permalink', get_permalink( ) ) )
    ) . '</p>',
  'comment_notes_after' => '<div class="clear"></div><div class="card-panel green"><span class="white-text">' .
    sprintf(
      __( 'You may use these <abbr title="HyperText Markup Language">HTML</abbr> tags and attributes: %s' ),
      ' <code>' . allowed_tags() . '</code>'
    ) . '</span></div>
    <p><button type="submit" id="submit-new" class="btn waves-effect waves-light right"><span class="white-text">'.__('Post Comment').'<i class="mdi-content-send right"></i></span></button><div class="clear"></div></p>'

);

if (is_user_logged_in()) {
	$commtarget = '#comment';
} else {
	$commtarget = '#author';
}


$replyargs = array(
	'walker'            => null,
	'max_depth'         => '',
	'style'             => 'ol',
	'callback'          => 'materialized_comment',
	'end-callback'      => null,
	'type'              => 'all',
	'reply_text'        => 'Reply',
	'page'              => '',
	'per_page'          => '',
	'avatar_size'       => 90,
	'reverse_top_level' => null,
	'reverse_children'  => '',
	'format'            => 'html5', //or xhtml if no HTML5 theme support
	'short_ping'        => false,
  'echo'     					=> true // boolean, default is true
);

?>
<div class="card">
<div id="comments" class="comments-area">
	<?php if ( have_comments() ) : ?>
		<span class="card-title">
		<h2 class="comments-title">
			 <a class="btn-floating btn-large waves-effect waves-light green right" href="<?php echo $commtarget; ?>"><i class="large mdi-editor-mode-edit"></i></a>
			<?php
				printf( _nx( 'One comment on &ldquo;%2$s&rdquo;', '%1$s comments on &ldquo;%2$s&rdquo;', get_comments_number(), 'comments title', 'materialized' ),
					number_format_i18n( get_comments_number() ), '<span>' . get_the_title() . '</span>' );
			?>
		</h2>
		</span>
		<div class="card-content">
		<?php if ( get_comment_pages_count() > 1 && get_option( 'page_comments' ) ) : // are there comments to navigate through ?>
		<nav id="comment-nav-above" class="navigation comment-navigation" role="navigation">
			<h2 class="screen-reader-text"><?php _e( 'Comment navigation', 'materialized' ); ?></h2>
			<div class="nav-links">

				<div class="nav-previous"><?php previous_comments_link( __( 'Older Comments', 'materialized' ) ); ?></div>
				<div class="nav-next"><?php next_comments_link( __( 'Newer Comments', 'materialized' ) ); ?></div>

			</div><!-- .nav-links -->
		</nav><!-- #comment-nav-above -->
		<?php endif; // check for comment navigation ?>

		<ol class="comment-list">
			<?php
				wp_list_comments($replyargs);
			?>
		</ol><!-- .comment-list -->

		<?php if ( get_comment_pages_count() > 1 && get_option( 'page_comments' ) ) : // are there comments to navigate through ?>
		<nav id="comment-nav-below" class="navigation comment-navigation" role="navigation">
			<h2 class="screen-reader-text"><?php _e( 'Comment navigation', 'materialized' ); ?></h2>
			<div class="nav-links">

				<div class="nav-previous"><?php previous_comments_link( __( 'Older Comments', 'materialized' ) ); ?></div>
				<div class="nav-next"><?php next_comments_link( __( 'Newer Comments', 'materialized' ) ); ?></div>

			</div><!-- .nav-links -->
		</nav><!-- #comment-nav-below -->
		<?php endif; // check for comment navigation ?>

	<?php endif; // have_comments() ?>

	<?php
		// If comments are closed and there are comments, let's leave a little note, shall we?
		if ( ! comments_open() && '0' != get_comments_number() && post_type_supports( get_post_type(), 'comments' ) ) :
	?>
		<p class="no-comments"><?php _e( 'Comments are closed.', 'materialized' ); ?></p>
	<?php endif; ?>
	</div>
	<?php comment_form($commentargs); ?>

</div><!-- #comments -->

Of course, we still need to add some supporting styles, so in your blue.css, add the following:

h2.comments-title, h3#reply-title {
  color: #fff;
  background: #2196f3;
  padding: 0 20px;
}

.btn, .btn-large {
  text-decoration: none;
  color: #FFF;
  background-color: #2196f3;
  text-align: center;
  letter-spacing: 0.5px;
  -webkit-transition: 0.2s ease-out;
  -moz-transition: 0.2s ease-out;
  -o-transition: 0.2s ease-out;
  -ms-transition: 0.2s ease-out;
  transition: 0.2s ease-out;
  cursor: pointer;
}

.depth-2 {
  border-left: 5px solid #64b5f6;
}

…and in your style.css, add the following:

h2.comments-title, h3#reply-title {
  font-size: 20px;
  margin: 0;
}

.comments-title .btn-large {
  margin-top: 10px;
}

ol.comment-list {
  margin: 0;
  padding: 0;
}

.comment-list li.comment {
  padding-top: 20px;
}

img.avatar.circle.left.avatar-90 {
  margin-right: 40px;
}

.comment-meta.commentmetadata {
  margin-bottom: 20px;
}

.divider {
  margin: 20px 0 0;
}

.comment ol.children {
  margin-left: 0;
  padding-left: 20px;
}

.comment ol.children ol.children {
  padding-left: 50px;
}

form#commentform {
  padding: 25px;
}

.comment-form p.form-submit {
  display: none;
}

Load it up in your browser, and check out your new Material powered comments! You’ll see that the comment reply button at the top of the comments section is conditional; logged in users will jump to the comment field, while not logged in users will jump to the author field.

We’re almost production ready, but there’s still some polishing that needs to be done. Let’s make the following additions to our style.css:

div#calendar_wrap {
  padding: 10px 15px;
}

ul#recentcomments {
  margin: 0 15px;
}

.widget form.search-form {
  margin: 0 20px 0 10px;
}

…and add the following to your blue.css:

.site-info {
  padding: 20px;
  background: #2196f3;
  color: #fff;
}

.site-info a {
	color: #fff;
}

That should add some padding and margin where needed in some of the standard sidebar widgets, and dress up the footer in our color scheme.

At this point, the theme can be loaded and run as is! You’ll still want to go through and make your own modifications, and you’ll likely find some widgets and other content types that still need some additional styling.

There are a few other steps you may want to take to flesh out your theme:

Most importantly, dig into the documentation, be creative, and have fun with it!

I hope you’ve enjoyed this tutorial, and look forward to your feedback in the comments.