The Underscores (_s) theme with Sass and Gulp Part 6: configuring the single post template

Untitled Document

==================================================================

On this page:

  1. The single post template.
  2. Modify the output of post metadata.
  3. Apply CSS to the post header.
  4. Apply CSS to typical post content.
  5. Configure and verify image alignments.
  6. Display "full-bleed" images on smaller screens.
  7. Style image captions.
  8. Style image galleries.
  9. Configure post navigation.

==================================================================

27. The single post template

Github repo branch: part6_27

The single post is arguably the core component of WordPress. The single post is what heralded in the Blogger Revolution, and most WordPress sites are built to be able to publish single posts. In this part 6, we'll work with the main content of the single post, that means:

  • the heading;
  • the metadata;
  • and post body content;

As we start working with single posts, we need to work with real content, and this is where the theme unit test data comes in. In this chapter, I'll be using three posts to test my content

  1. 'Hello world!', which displays a one sentence post, followed by one comment.
  2. 'Markup: HTML Tags and Formatting' which displays all the HTML tags and all the formatting options the user can insert into a WordPress post.
  3. 'Markup image alignments', which show us all the different ways we can display images in a post, both in terms of alignments and with and without image captions.

I have created a button in the main menu with the above mentions postst as submenu items so that I can easily see the results of the styling which I am going to do. I suggest you do the same.

Now, let's take a look at the template that powers the single post, and up here in the WordPress tool bar, you can see that is a file called 'single.php'. Single.php is a structural template file. That means it's the one that's called by the WordPress template hierarchy anytime a single post is displayed, and single.php strings together all the different components that make up the full display of a single post.


<?php
  /**
  * The template for displaying all single posts
  *
  * @link https://developer.wordpress.org/themes/basics/template-hierarchy/#single-post
  *
  * @package Pre_Underscores
  */
get_header(); ?>
 <div id="primary" class="content-area">
  <main id="main" class="site-main">
 <?php
  while ( have_posts() ) : the_post();
 get_template_part( 'template-parts/content', get_post_type() );
 the_post_navigation();
 // 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;
 endwhile; // End of the loop.
  ?>
 </main><!-- #main -->
  </div><!-- #primary -->
<?php
  get_sidebar();
  get_footer();

  
code 6.27.1. single.php
  • Off the top, the function get header calls the file header.php.
  • then we set up some basic structures. We have a div with id primary and class content area that wraps around a main with id main and class site main.
  • Inside the main, we run what's know as the WordPress loop, which says, for as long as I have posts, give me the next available post and then do the following to it: get the template part, template parts content, and run that content.
  • Then display the post navigation.
  • Then, if comments are currently on, display the comments template.
  • Then we end the while loop, close the main and the div.
  • And finally call sidebar.php and footer.php.

All the content is stored inside a separate file called content.php, which you'll find under template parts. In template parts, you have a series of different files: you have:

  • content.php. content.php is used both for single posts and for index and archive pages
  • content-none.php. This is used anytime there is no content to display.
  • content-page.php. This is used when we want to display page content.
  • content-search.php. This returns search results

Inside content.php, we have the post itself, and here off the top, we start the post with an article that wraps around everything, then we have a header, with a class entry header, and inside the header, will display the title of the post, followed by the post metadata.

<?php
  /**
  * Template part for displaying posts
  *
  * @link https://developer.wordpress.org/themes/basics/template-hierarchy/
  *
  * @package Pre_Underscores
  */
?>
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
  <header class="entry-header">
  <?php
  if ( is_singular() ) :
  the_title( '<h1 class="entry-title">', '</h1>' );
  else :
  the_title( '<h2 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h2>' );
  endif;
 if ( 'post' === get_post_type() ) : ?>
  <div class="entry-meta">
  <?php pre_underscores_posted_on(); ?>
  </div><!-- .entry-meta -->
  <?php
  endif; ?>
  </header><!-- .entry-header -->
 <?php pre_underscores_post_thumbnail(); ?>
 <div class="entry-content">
  <?php
  the_content( sprintf(
  wp_kses(
  /* translators: %s: Name of current post. Only visible to screen readers */
  __( 'Continue reading<span class="screen-reader-text"> "%s"</span>', 'pre_underscores' ),
  array(
  'span' => array(
  'class' => array(),
  ),
  )
  ),
  get_the_title()
  ) );
 wp_link_pages( array(
  'before' => '<div class="page-links">' . esc_html__( 'Pages:', 'pre_underscores' ),
  'after'  => '</div>',
  ) );
  ?>
  </div><!-- .entry-content -->
 <footer class="entry-footer">
  <?php pre_underscores_entry_footer(); ?>
  </footer><!-- .entry-footer -->
  </article><!-- #post-<?php the_ID(); ?> -->
  
code 6.27.1. template-parts/content.php

Now because this template is used both for single posts and for archive pages, we have to run a conditional statement that says, if we are on a single post, then wrap the title in an h1 with a class entry title. But, if we're on an archive page or an index page, wrap the title in an h2 with a class entry title, and give it a link that points to the post itself, so people can actually navigate to the post. Then, we have another conditional that tests to make sure this is a post post type, and if it is, we display the entry meta.

The reason why the main content is broken out into a separate file is pretty obvious. That way, we can use the same file for different purposes throughout the site. As we move forward in the course, we'll actually make a custom template just for single posts, because there are some structural considerations we have to keep in mind, but this structure here, having content.php used both for single posts and for archive pages is pretty common.

==================================================================

28. Modify the output of post metadata

Github repo branch: part6_28

As I've explained before, when I worked with Wordpress things I like to start at the top and work my way down in modules. And the first module in the single post template is the header. In the header for my theme, I want to display a list of categories at the very top, above the post title. At the moment though list of categories is in the entry footer. So what I need to do is grab whatever function is generating that list of categories, break it out, and then call it at the top of the post instead. You'll remember from the previous part, that the contents of the post itself are powered by the template-parts/content.php (see code 6.27.1). So we'll open that file in the IDE (I use PHPStorm) then I'll scroll down and find the pre_underscores_entry_footer(). I don't know where this function is, in my theme, but I can get to it by simply holding down CTRL or command and clicking on the function name. And it leads me to thr file inc/template-tags.php. On line 48 we find the pre_underscores_entry_footer() function. This is the function that outputs the entire string of text for the entry footer.

I want to create a new function, pre_underscores_the_category_list() and copy/past the lines from pre_underscores_entry_footer() that i need

function pre_underscores_the_category_list() {
// Hide category and tag text for pages.
if ( 'post' === get_post_type() ) {
/* translators: used between list items, there is a space after the comma */
$categories_list = get_the_category_list( esc_html__( ', ', 'pre_underscores' ) );
if ( $categories_list ) {
/* translators: 1: list of categories. */
printf( '<span class="cat-links">' . esc_html__( '%1$s', 'pre_underscores' ) . '</span>', $categories_list ); // WPCS: XSS OK.
		}
	}
}

Now we can test to make sure this function actually works by saving templatetags.php, grabbing the name of the new function, going to content.php and just placing in the function where we want the category list to appear. So that would be right here at the top, directly inside header on line 12.

<?php
  /**
  * Template part for displaying posts
  *
  * @link https://developer.wordpress.org/themes/basics/template-hierarchy/
  *
  * @package Pre_Underscores
  */
?>
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
  <header class="entry-header">
  <?php pre_underscores_the_category_list();?>
  ...
  

Save that. Go back to the front end, and see what happens. And, all-right! I have a category list at the top of the page now.

w we get a full list of categories, separated by commas. Now that we have the category list at the top, we really should remove it from the footer. So the final step is to go back to content.php, scroll down and find the original function pre_underscores_entry_footer(), again, CTRL or command click on it, to get to the function and here, I'll simply remove the code for displaying the category list entirely, because we've broken it out into a separate function. So, from pre_underscores_entry_footer(), remove the lines:

/* translators: used between list items, there is a space after the comma */
$categories_list = get_the_category_list( esc_html__( ', ', 'pre_underscores' ) );
if ( $categories_list ) {
/* translators: 1: list of categories. */
printf( '<span class="cat-links">' . esc_html__( 'Posted in %1$s', 'pre_underscores' ) . '</span>', $categories_list ); // WPCS: XSS OK.
}

Finally in the comment

// Hide category and tag text for pages.

remove 'category and'

// Hide  tag text for pages.

If I go into the post and add some tags abd then again view the post we see that we have categories at the top and tags on the bottom.

==================================================================

29. Apply CSS to the post header

Github repo branch: part6_29

Still in the header, I also want to change the post metadata. I want to see: 'written by', then the author name, then 'published', then the publishing date and then I want to add a link to the comment section that says either, no comments, one comment, or the number of comments. And finally I want to move the edit link up into the header meta section.

Just like before I'll start in content.php to figure out what is generating this content right now. I'll scroll down until the entry meta section, and here we see another function, pre_underscores_posted_on(). I'll hold on control or command to go to the function. This function is in templatetags.php just like the previous one was. And here we have all the code that generates what you see on the front end.

 function pre_underscores_posted_on() {
  $time_string = '<time class="entry-date published updated" datetime="%1$s">%2$s</time>';
  if ( get_the_time( 'U' ) !== get_the_modified_time( 'U' ) ) {
  $time_string = '<time class="entry-date published" datetime="%1$s">%2$s</time><time class="updated" datetime="%3$s">%4$s</time>';
  }
 $time_string = sprintf( $time_string,
  esc_attr( get_the_date( 'c' ) ),
  esc_html( get_the_date() ),
  esc_attr( get_the_modified_date( 'c' ) ),
  esc_html( get_the_modified_date() )
  );
 $posted_on = sprintf(
  /* translators: %s: post date. */
  esc_html_x( 'Posted on %s', 'post date', 'pre_underscores' ),
  '<a href="' . esc_url( get_permalink() ) . '" rel="bookmark">' . $time_string . '</a>'
  );
 $byline = sprintf(
  /* translators: %s: post author. */
  esc_html_x( 'by %s', 'post author', 'pre_underscores' ),
  '<span class="author vcard"><a class="url fn n" href="' . esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ) . '">' . esc_html( get_the_author() ) . '</a></span>'
  );
 echo '<span class="posted-on">' . $posted_on . '</span><span class="byline"> ' . $byline . '</span>'; // WPCS: XSS OK.
 }

And on first look, this looks pretty intimidating, because on the front end, all we have is "Posted on" then a publishing date, and "By Admin", and on the back end we have this massive amount of code.

Well, what's happening here is, the theme is providing all the necessary information to the browser. This is both for the browser and for the search engines. So this simple element here that displays the publishing date, is actually a very complex piece of HTML. If we inspect this element for one second, you'll see there's a time element inside. In that time element, we have a computer readable time, and a human readable time, and there's also an additional time element that's been hidden using CSS, that's another computer readable time and a human readable time.

This is the updated time, so any time you update a post this will change even though the original publishing date stays the same.

All i have to do now is change 'Posted on' in 'Published' and change 'by' in 'written by', and change

echo '<span class="posted-on">' . $posted_on . '</span><span class="byline"> ' . $byline . '</span>'; // WPCS: XSS OK.
 

in:

echo '<span class="byline"> ' . $byline . '</span> <span class="posted-on">' . $posted_on . '</span>'; // WPCS: XSS OK.

Save, and go back to the browser, and now it says "Written by Admin" "Published" and the publishing date.

The next step is to grab the comment information. But I don't see any comment information here right now. However, if I navigate to the front page of the site and scroll down until I find the Hello World post, you'll notice that in the footer it says "One Comment". So this is a conditional function that only displays in index pages right now. Knowing that it's in the footer, I can scroll down inside template tags and look into the function pre_underscores_entry_footer().

if ( ! is_single() && ! post_password_required() && ( comments_open() || get_comments_number() ) ) {
echo '<span class="comments-link">';
comments_popup_link(
sprintf(
wp_kses(
/* translators: %s: post title */
__( 'Leave a Comment<span class="screen-reader-text"> on %s</span>', 'pre_underscores' ),
array(
'span' => array(
'class' => array(),
),
)
),
get_the_title()
)
);
echo '</span>';

I remove ! is_single() && because I also want comment notification on single pages, and paste it directly after

echo '<span class="byline"> ' . $byline . '</span> <span class="posted-on">' . $posted_on . '</span>'; // WPCS: XSS OK.

in the same template-tags.php file.

The same trick I do with the edit link.

Save a final time, and now I have the metadata structure I want.

==================================================================

30. Apply CSS to typical post content

Github repo branch: part6_30

Working with themes, I have a very specific process in place to ensure all my content is accessible first and then looks good. That means, any time I have a new module, I start by mapping out all the contents within that module and make sure the HTML is structured correctly, and everything is working, and then I apply CSS to it, and if I want to, also some JavaScript. Now that we have all the content in place in the header, it's time to apply styling to it.

The styling for the main post content in sass/site/primary/_posts-and-pages.scss. Here, you'll find existing rules for posts-and-pages, so I'm just going to build on what's already there. I'll keep the logical structure of this intact. Since I'm working on the header, the header should go directly after age entry. Age entry is the overall post content.

First I made a change in the sass/variables-site/_colors.scss file. I added:

$color__interactive: #b51c35; // Links, highlights, etc.

The fully changed code in sass/site/primary/_posts-and-pages.scss is shown blow in code 6.30.1

.sticky {
  display: block;
  }
.hentry {
  margin: 0 0 1.5em;
  }
.entry-header {
  font-family: $font__sans;
  }
.cat-links {
  font-size: 90%;
  font-weight: 700;
  
  a {
  text-decoration: none;
  text-transform: uppercase;
  color: $color__interactive;
  
  &:focus,
  &:hover {
  background-color: $color__interactive;
  color: white;
  }
  }
  }
.entry-title {
  margin: .125em 0 .25em;
  font-size: 2.5em;
  
  @media screen and (min-width: $query__medium) {
  font-size: 3em;
  }
  
  a {
  text-decoration: none;
  color: black;
  
  &:focus,
  &:hover {
  border-bottom: 5px solid $color__interactive;
  }
  }
  }
.entry-meta {
  font-size: 90%;
  
  a {
  font-weight: 700;
  text-decoration: none;
  color: black;
  
  &:focus,
  &:hover {
  color: black;
  border-bottom: 3px solid $color__interactive;
  }
  }
  }
.byline {
  &::after {
  content: "|";
  margin: 0 .5em;
  }
  }
.comments-link,
  .edit-link {
  &::before {
  content: "|";
  margin: 0 .5em;
  }
  }
.byline,
  .updated:not(.published){
  display: none;
  }
.single .byline,
  .group-blog .byline {
  display: inline;
  }
.page-content,
  .entry-content,
  .entry-summary {
  margin: 1.5em 0 0;
  }
.page-links {
  clear: both;
  margin: 0 0 1.5em;
  }
  
code 6.30.1. The new sass/site/primary/_posts-and-pages.scss

When people start using your theme, they'll add a variety of different types of content to your posts, and they'll format that content in a variety of different ways. That's why we have this post: Markup: HTML Tags and Formatting, in the WordPress theme unit test data which we imported in Part 1.4. This post showcases all the different types of tags and formatting that may be applied to content, and we can use it to style all the content in WordPress to make it match with our overall design.

First I made a change in the sass/variables-site/_colors.scss file. I changed the below mentioned variables to black:

$color__text-screen: #000;
$color__link: #000;
$color__text-main: #000;

The fully changed code in sass/navigation/_links.scss is shown blow in code 6.31.2

I have also made some changes to:

  • sass/navigation/_links.scss. where I added styles for .page-content,.entry-content,.entry-summary and .comment-content
  • sass/elements/_elements.scss. This is where the main elements that are displayed on the page are defined. In particular, I've taken out some of the style rules because I find that they are cluttering-up the design.
  • sass/typography/_copy.scss. The actual copy for the text, itself, is styled in this file. Here, the only change I've made is adding custom styles for the blockquotes.
  • sass/elements/_tables.scss. Tables need to be styled because, out of the box, Underscores does not style tables at all and they look quite awful.
  • sass/elements/_lists.scss. The same can be said for lists, and they need some additional styling
  • sass/typography/_headings.scss.

Now that you know where my changes are, I recommend you go through all these files and see what is going on in them and how they link together. Then, go check out this post and take a look at how the different tags and elements have been styled and formatted so you can add your own styles and formats later, in your own themes. Remember: use the post 'Markup: HTML Tags and Formatting' for checking most of the changes I made.

==================================================================

31. Configure and verify image alignments

Github repo branch: part6_31

Once we've sorted the styling for regular text using the 'HTML tags and formatting' post, we can move on to images and here we'll start with image alignment. In the post called 'markup image alignment', you will see all the different types of alignments and configurations we can have for images. Scrolling down in the 'markup image alignment' post, we have a series of images and for each image it'll have a description of how that image is aligned.

  1. A centered image.
  2. A left aligned image.
  3. One that has no alignment meaning its just displayed in the content in line.
  4. one that's right aligned.

Then we have a series of images with captions, with the exact same setup.

  1. A centered image with a caption.
  2. A left aligned image with a caption.
  3. no alignment image with a caption.
  4. A right aligned image with a caption.

If I go to the poast 'Markup: Image Alignment' in the back end I can look at one of these images and the css classes of the images. I can also click on any image and from here I can change the alignment by simply clicking on the buttons that appear. You see we have align left, center, right, and no alignment.

What WordPress does when you apply alignment to an image is it applies a class to the image element: 'aligncenter', 'alignleft', 'alignright'.

If the image has a caption, this changes a little bit. So if we inspect an image with a caption, you'll see that the class is applied to the figure that wraps around the image, and the figcaption, again 'aligncenter', 'alignleft', 'alignright' .

In our Sass setup, the alignment rules are found in sass/modules/_alignments.scss. Here you see alignleft, alignright, and aligncenter. The reason why these are so generic is you can choose to apply them to other elements. For example I've seen theme developers use the alignments classes to align blockquotes so that blockquotes turn into pullquotes in the posts. For my purposes, these settings are exactly what I want. I'm not going to touch them at all. For your themes you may want to change them, and now you know where to do that (see code 6.31.1)

.alignleft {
  display: inline;
  float: left;
  margin-right: 1.5em;
  }
.alignright {
  display: inline;
  float: right;
  margin-left: 1.5em;
  }
.aligncenter {
  clear: both;
  @include center-block;
  }
  
code 6.30.1. The new sass/site/primary/_posts-and-pages.scss

==================================================================

32. Display "full-bleed" images on smaller screens

Github repo branch: part6_32

==================================================================

33. Style image captions

Github repo branch: part6_33

==================================================================

34. Style image galleries

Github repo branch: part6_34

==================================================================

35. Configure post navigation

Github repo branch: part6_35

Leave a comment