prygara.com – html, css, javascript, testing

About this site

Current version of this site uses Underscores starter theme. Depending on the page or view, the following templates are used:

Layout

The site uses sticky footer layout with flexbox. I used the one by Philip Walton. Let’s take a look at HTML and CSS to see how it is implemented.

<body>
  <header class="l-site-header">…</header>
  <main>…</main>
  <footer class="l-site-footer">…</footer>
</body>

Our body tag serves as a container for header, main and footer. I use SASS and follow SMACSS for organizing CSS so my layout classes are pre-fixed with l-.

body {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}
.l-constrained {
    max-width: 82rem;
    padding: 0 5px;
    margin: 0 auto;
}
.l-site-footer { 
    margin-top: auto;
}

Since we give our body tag display: flex with flex-direction: column 2 things happen:

1.Our body element becomes flex container and all body children – header, main and footer – become flex items.

2.Since we change default flex-direction value from row to column, direction of the main axis changes and now runs from top to bottom.Consequently, our flex items begin to lay out in the direction of the main axis.

Very important is to set min-height:100vh on our container as this is needed if we run main axis in the vertical direction.

I put .l-constrained class on the main tag so it keeps main content at the maximum of 82rem which translates to 820px. I have font-size: 62.5%; //=10px set on the html root so 82rem x 10 = 820px.

There’s also margin-top: auto set on the footer .l-site-footer. It takes up all of the available space in the direction it is set to and therefore makes footer stick to the bottom of the page even if there’s not enough content.

Logo and navigation

I came across this responsive menu that is based on the checkbox hack method. Here’s how it is implemented on the current version of my site.

Markup for the logo and site navigation is generated by the following code in the header.php.

<nav class="nav-header">
      <?php 
        if ( function_exists( 'the_custom_logo' ) ) {
          the_custom_logo();
        }
      ?>
      <input type="checkbox" id="menu-toggle" />
      <label for="menu-toggle" class="label-toggle"></label>
       <?php
          wp_nav_menu(
            array(
              'theme_location' => 'menu-1',
              'menu_id'        => 'primary-menu'
            )
          );
        ?>
</nav>

Markup generated by the header.php.

<header class="l-site-header">
   <nav id="site-navigation" class="nav-header">
       <a href="..."><img width="100" height="100" src="..." class="custom-logo"/></a>
       <input type="checkbox" id="menu-toggle" />
       <label for="menu-toggle" class="label-toggle"></label>
       <div class="menu">
	  <ul>
            <li class="current_page_item"><a href="https://test.test/">work</a></li>
            <li><a href=".../blog/">blog</a></li>
            <li><a href=".../archive/">archive</a></li>
            <li><a href=".../about/">about</a></li>
            <li><a href=".../contact/">contact</a></li>
	  </ul>
       </div>
   </nav>
</header>

Our nav tag with .nav-header class above is a wrapper for the logo, toggle (ham icon) and menu. The following declaration block centers all three vertically and horizontally.

.nav-header { /*wrapper*/
    display: flex;
    justify-content: space-between;
    align-items: center;
}

WordPress custom logo function is used to display custom logo. I further used auto generated .custom-logo class to add some padding and margin to the logo.

I didn’t use any extra parameters to change or remove the wrapper div or container class in the wp_nav_menu function. So it defaults to the ul menu wrapped in a div with .menu class. Since menu items are list items they appear stacked vertically and have bullet points. The following declaration block removes list bullets and makes the menu horizontal.

.menu {
    ul {
      display: flex;
      justify-content: space-between;
      width: 45rem;
      list-style-type: none;
      text-transform: uppercase;
    } 
}

Mobile version of the menu is based on the following input/label pair linked via ID menu-toggle.

<input type="checkbox" id="menu-toggle"/>
<label for="menu-toggle" class="label-toggle"></label>

We hide both with the following CSS. However, when we click the hidden label we are still able to toggle the input value because they are linked via id="menu-toggle". This gives us the opportunity to use :checked state of the input.

#menu-toggle {
    display: none;
} 

.label-toggle {
    display: none;
}

Next we add the following media query. It targets screens that are 960px or less.

@media screen and (max-width: 960px) {
	      ...	
}

The three dots represent the following declaration blocks included within the 960 media query. We remove bottom border from the header.

.l-site-header {
      border: none;
}

We add flex-wrap: wrap so our navigation moves to a new line. As a result we have an empty space to move the ham icon .label-toggle all the way to the right edge.

.nav-header {
      display: flex;
      flex-wrap: wrap;
}

Next we add the following flex shorthand where we set flex-grow to 1 which means our menu takes up all available space within the container. We set flex-shrink to 0 which means our menu doesn’t shr ink (default is 1). We also set flex-basis to 100% so it would take 100% width of its parent.

.menu {
    flex:1 0 100%;
} 

We then target our ul containing menu items. We give our ul and li diplay: block so our menu items stack vertically. There’s max-height: 0 and opacity: 0 which we will transition in the next declaration block.

.menu { 
    
    ul {

      display: block;
      width: 100%;
      height:auto;
      max-height: 0;
      padding: 0;
      margin: 0;
      background-color: $color-6; 
      transition: max-height .5s ease, opacity .5s ease;
      opacity: 0;
      list-style-type: none;
      text-align: center;
      visibility: hidden; 
      overflow: hidden;
  }

   li {
      display: block;
      padding: 1.5em 0;
      border-bottom: 1px solid $color-7; 
      font-size: 1em;
      color: $color-1;
  }

}

We now transition our max-height:0 and opacity:0 respectively using CSS :checked pseudo-class.

#menu-toggle:checked ~ .menu > ul {
      height:auto;
      max-height:40rem;
      opacity: 1;
      visibility: visible;
} 

The last ruleset creates a visible ham icon.

.label-toggle {
      display: block; 
      margin-left: auto;
      float: none;
      width: 35px;
      height: 30px;
      background: linear-gradient
      (to bottom, #888 0%, #888 20%, transparent 20%,    
      transparent 40%, #888 40%, #888 60%, transparent 60%, 
      transparent 80%, #888 80%, #888 100%);
      cursor: pointer;
}

There’s a WordPress generated class .current_page_item which I use to put green underline on currently selected menu item. It appears that class is generated by wp_list_pages.

.current_page_item > a {
    border-bottom: .2rem solid $color-4;
} 

The above .current_page_item rule is the last one that sits within 960px media query.

When you hover over individual menu items, there’s some left-to-right underline transition going on. I borrowed that CSS transition technique from this tutorial.

The first step is to remove default link underline and put position: relative on the anchors as they are the container for our absolutely positioned underline (please, see border-bottom). I used WordPress generated class .page_item to target the links.

.page_item > a:link,
.page_item > a:visited {
    position:relative;
    text-decoration: none;
}

.page_item > a:hover,
.page_item > a:active {
    text-decoration: none;
}

Next I used ::after pseudo-element to add border-bottom to links. I was curious as to why we specifically need an empty content: "" property in the following ruleset. The most common scenario when we use ::after is when we place our content to be inserted in between the quotation marks of content: "". Example: content: "Hello"; Eventually I came across this post on Stackoverflow which shed some light on using CSS content property with empty value.

As you can see below we use position: absolute to position our border-bottom within its container (please, see .page_item > a rules above). The underline is not visible because we pushed it all the way to the left by setting right offset to 100%. There’s also transition: right .2s; property.

.page_item > a:link::after,
.page_item > a:visited::after {
    content: "";
    position: absolute;
    right: 100%;
    left: 0;
    bottom: -2.3px;
    border-bottom: .2rem solid $color-4;
    transition: right .2s;
    backface-visibility:hidden;
}

In order for our animation to work, we add the following rule where we target our pseudo-element on hover. So now what happens is our underline transitions from left to right at the speed of .2s. This is because right: 0; stretches it all the way to the right edge of the container.

.page_item > a:hover::after{
    right: 0;
}

Work page

To build the work page I borrowed some code from this WordPress tutorial that teaches how to use Custom Post Types to build a portfolio page. It was a three-step process.

  1. Create portfolio.php where we register custom post type, labels, taxonomies, metaboxes and columns for the admin screen.
  2. Load portfolio.php into functions.php via require get_template_directory() . '/inc/portfolio.php';
  3. Create WordPress template that would handle the output for the work page. I used front-page.php as this is the default template for front page.

Create portfolio.php

Here we use WordPress register_post_type function to register Custom Post Type ‘portfolio’ that handles labels and give this post type all standard post capabilities via $args array. We use add_action function to call our newly created function when the init hook is executed.

//add custom post type

function portfolio_register () {

  $labels = array (
  
          'name'          => __('Projects'),
          'singular_name' => __('Project'),
          'add_new'       => __('Add New Project'),
          'add_new_item'  => __('Add New Project'),
          'edit_item'     => __('Edit Project'),
          'new_item'      => __('New Project'),
          'view_item'     => __('View Project'),
  ); 

  $args   = array (

          'labels'    => $labels,
          'public' => true,
          'show_ui'=> true,
          'capability_type' => 'post',
          'hierarchical' => false,
          'rewrite' => true,
          'supports' => array('title', 'editor', 'thumbnail') 
  );

      register_post_type ('portfolio', $args);

}
 
add_action ('init', 'portfolio_register');

Next we register ‘project-type’ taxonomy and assign it to our ‘portfolio’ custom post type via register_taxonomy function. Again the function is called via add_action. The following code adds ‘Project Types’ label to WordPress admin screen and gives an option to create categories specific to our newly registered ‘portfolio’ post type. Those project types we create are not associated with default categories and tags. More information about custom taxonomies can be found here.

//add custom taxonomy

function portfolio_taxonomies() {
  $labels = array(
          'name'          => __('Project Types'),
          'singular_name' => __('Project Type')
  );

  $args = array(
          'labels'    => $labels,
          'hierarchical' => true,
          'rewrite' => true

  );
  register_taxonomy('project-type', 'portfolio', $args);
}

add_action ('init','portfolio_taxonomies', 0);

The following code adds a separate field to the admin screen where we can enter and save project URLs.

//add metaboxes 


function portfolio_meta_box() {

  add_meta_box ('project-meta', 'Add Project Link', 'portfolio_meta_options',  'portfolio', 'side', 'low');
  
  }
  
  add_action("admin_init", "portfolio_meta_box");
  
  function portfolio_meta_options(){
  
        global $post;
  
        if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) return $post_id;
        $custom = get_post_custom($post->ID);
        $link = $custom ['project-link'][0];
  ?>
  
  <input name="project-link" value="<?php echo $link; ?>" />
  <?php
 }

Let’s break it down into smaller parts.

The following code saves URLs that we enter into ‘Add Project Link’ metabox.

//save custom meta boxes when the post is saved


function save_project_link (){

  global $post;

  if(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE )  {
     return $post_id;
  }

  else  {

    update_post_meta ($post->ID, 'project-link', $_POST['project-link']);
  }
}

add_action ('save_post', 'save_project_link');

Add and display admin columns for our ‘portfolio’ Custom Post Type

The following code adds column header names to the ‘portfolio’ Custom Post Type table.

//add custom columns  

function project_register_columns($columns) {
  $columns = array(
      'cb' => '<input type=\'checkbox\' />',
      'title' => 'Project',
      'link' => 'Link',
      'type' => 'Project Type',
      'date' => 'Date'
  );
  return $columns;

}

add_filter('manage_edit-portfolio_columns', 'project_register_columns');

The first column we add is checkbox input followed by Project, Link, Project Type and Date. They are added and returned via array as key/value pairs in our project_register_columns function. Then we attach our function to manage_edit-portfolio_columns filter hook.

Next we have the PHP switch statement that refers to two keys, ‘link’ and ‘type’, we defined in our previous function.

//display custom columns

function add_columns_content ($columns) {
  global $post;
  switch ($columns)
  {
   
    
      case 'link':
          $custom = get_post_custom();
          echo $custom['project-link'][0];
          break;

      case 'type':  
            echo get_the_term_list($post->ID, 'project-type', '', ', ','');  
            break;  

  }

}

add_action('manage_posts_custom_column', 'add_columns_content');

Those keys are used to display ‘Link’ and ‘Project Type’ column names in the admin. Notice that the number of ‘cases’ in the switch statement doesn’t match the number of columns in our $columns array. As mentioned in the Step 4 of the tutorial this is because for certain keys, like cb and title, WordPress has default values that we don’t want to overwrite.

We run our function via manage_posts_custom_column action hook.

Display ‘portfolio’ Custom Post Type content on work page

To output projects on work page, I created front-page.php. According to Template Hierarchy if front-page.php is present, it would override other templates. Here’s a good read that explains the difference between front-page, home.php and other templates that potentially could be used for front page display.

Let’s examine the code that goes into front-page.php template. I used WordPress WP_Query class to create a custom loop.

First we set up portfolio_args variable with 3 parameters: post_type, post_status and posts_per_page. It means our custom query would output posts published under custom post type “portfolio” as it is the value for out post_type. The negative value -1 in the posts_per_page means we request to show all posts .

<?php
    $portfolio_args  = array(
        'post_type'         => 'portfolio',
        'post_status'       => 'publish',
        'posts_per_page'    => -1
    );

Next we create a new instance of WP_Query class ,$portfolio_query, and use it in our custom loop. We base our custom query on $portfolio_query instance that uses -> object operator to call have_posts() and the_post() methods to check if there are any posts in “portfolio” Custom Post Type. If there are posts, then we output a link and thumbnail for that post. We also use wp_reset_postdata() to reset both main and custom loop.

$portfolio_query = new WP_Query( $portfolio_args );

    if( $portfolio_query->have_posts() ) : ?>
    
    <main class="l-work">
        
       <?php while ( $portfolio_query->have_posts() ) : $portfolio_query->the_post(); ?>
          <div>    
                <a href="<?php echo get_permalink( $post->ID ); ?>">
                <?php echo get_the_post_thumbnail( $post->ID ); ?> 
               </a>

          </div>
        <?php endwhile; ?>
        
    </main>
    
<?php endif; wp_reset_postdata(); ?>

Our custom loop outputs the following HTML.

<main class="l-work">
    
    <div>    
         <a href="...">
            <img width="600" height="450" src="..." /> 
         </a>
    </div>
                  
    <div>    
         <a href="...">
            <img width="600" height="450" src="..." /> 
         </a>
    </div>

    <div>    
         <a href="...">
            <img width="600" height="450" src="..." /> 
         </a>
    </div>
                                
</main>

I used CSS grid for work page layout. First we create three column grid by declaring display: grid on the container main and using repeat notation with grid-template-columns.

.l-work {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1rem;
}

Next we make images responsive keeping their aspect ratio.

.l-work img,
.l-view img {
    display: block;
    width: 100%;
    height: auto;
}

Then I just used two media queries to change number of columns on smaller screens.

@media screen and (max-width: 1000px) {
  .l-work {
      grid-template-columns: repeat(2, 1fr);
  }

}

@media screen and (max-width: 680px) {
  .l-work {
      grid-template-columns: repeat(1, 1fr);
  }
}

Blog page

Underscores uses index.php and content.php to output blog posts. There’s the loop in the index.php that checks if there are any posts. If there are posts, it uses get_template_part to pull in content.php that outputs posts as excerpts.

index.php

<main class="l-constrained">
  
    <?php
   	if ( have_posts() ) :
	if ( is_home() && ! is_front_page() ) :
     ?>
    <header>
     <h1 class="page-title screen-reader-text"><?php single_post_title(); ?></h1>
    </header>
	<?php
 endif;

		/* start the loop */
		while ( have_posts() ) :
		the_post();
      
		get_template_part( 'template-parts/content', get_post_type() );
        
         endwhile;

        the_posts_navigation();

      else :

	get_template_part( 'template-parts/content', 'none' );
 endif;?>

</main>

content.php

<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 echo get_the_date();?>
              </div><!-- .entry-meta -->
            <?php endif; ?>
        </header><!-- .entry-header -->

	<?php my_wp_theme_post_thumbnail(); ?>

	<div class="entry-content">
		<?php the_excerpt();?>
	</div><!-- .entry-content -->

   <div class="read-more">
      <a href="<?php echo esc_url( get_permalink() ) ?>" rel="bookmark">
        read more
      </a>
   </div>

</article><!-- #post-<?php the_ID(); ?> 

We have the following HTML output by index.php and content.php.

<main class="l-constrained">
  
<header>
<h1 class="page-title screen-reader-text">blog</h1>
</header>
				
      <article class="blish format-standard hentry category-html-and-css">

        <header class="entry-header">
            <h2 class="entry-title"><a href="..." rel="bookmark">About this site</a></h2>              
	<div class="entry-meta">
                	December 24, 2021              
	</div><!-- .entry-meta -->
        </header><!-- .entry-header -->

	<div class="entry-content">
	<p>Current version of this site uses Underscores starter theme...</p>
	</div><!-- .entry-content -->

   <div class="read-more">
      <a href="..." rel="bookmark">
        read more
     </a>
   </div>

</article><!-- #post-2141 -->

</main><!-- #main -->

Blog page content sits within .l-constrained CSS class as you can see from the above markup. It keeps blog content at 820px width and centers it on the page. Most of the blog content stays in the normal flow and there’s no additional CSS styling applied.

.l-constrained {
    max-width: 82rem;/*=820px*/ 
    padding: 0 5px;
    margin: 0 auto;
}

Archive page

Yearly archive of blog posts lives within page-archive.php. I came across this post on WordPress StackExchnage and used the code in my page-archive.php.

Here’s the content of page-archive.php with some explanations. First we instantiate WP_Query class with an array of parameters.

<?php 
   $posts = new WP_Query (
          array (
            'post_type'=>'post', 
            'post_status'=>'publish', 
            'posts_per_page'=>-1
            )
        ); 

Next we have our have_posts() function that checks if there are any published posts. If there are posts, open an unordered list and get the year get_the_time('Y'). The loop continues. The if/else statement uses current_post === 0 as a condition. It means if the current post is at index 0 (zero means it’s a first post, second post is 1 etc.), then open a list item that contains post year wrapped in level two heading. Then we have the elseif portion that checks if $last_year is not identical to $year. If that condition is true, close previous list item and open a new list item containing the new year. Post permalinks and dates are placed within a separate div. Then there’s the final if statement that echos closing list item once all post were displayed.

if ( $posts->have_posts() ) : ?>
 
   <ul>
      <?php while ( $posts->have_posts() ) : $posts->the_post(); 
            $year = get_the_time('Y');

           if ($posts->current_post === 0) 

           //open li item
           printf( '<li><h2>%s</h2>', $year ); 
           //close previous list item, open the next one
           elseif ($last_year !== $year)
           printf( '</li><li><h2>%s</h2>', $year ); 
      ?>
   
<div>
           <a href="<?php the_permalink() ?>"><?php the_title() ?></a>
           <div class="dt"><?php the_time( ' d M Y' ) ?></div>
   </div>
        
       
<?php

            if ( ( $posts->current_post + 1 ) === $posts->post_count )
            echo '</li>'; // Always close <li> at the end of the loop
            $last_year = $year;
            endwhile;
         ?>
   </ul>
 
<?php endif ?>

Above PHP produces the following HTML.

<main class="l-constrained l-archive">
<ul>
   <li><h2>2021</h2>      
    
<div>
       <a href="....">About this site</a>
       <div class="dt"> 24 Dec 2021</div>
    </div>

   </li>
</ul>
</main>

I have two CSS classes .l-constrained and .l-archive for the archive. They are applied to the main container. Archive page wouldn’t grow more than 820px.

.l-constrained {
    max-width: 82rem;/*=820px*/ 
    padding: 0 5px;
    margin: 0 auto;
} 

Because post titles have different length, I used width: 100% so the titles stretch on one line at max-width: 820px .

.l-archive {
    width: 100%;
}

Post title and post date are wrapped in a div with display: flex and justify-content: space-between which distributes available space between a title and a date.

.l-archive div {
    display: flex;
    justify-content: space-between;
    margin-bottom: 0.5rem;
}

There’s a media query at 500px that resets containing div to display: block on screens 500px and less.

@media all and (max-width: 500px) {
  .l-archive li > div { 
      display: block; 
      margin-bottom: 1.5em;
  }
}

About page

About page is handled by the following PHP in page-about.php. This is a simple loop with the_content() template tag that outputs an image and the text that follows the image.

<main class="l-constrained">

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

     <article class="l-about">
        <?php the_content();?>

     </article>

 <?php endwhile;
 endif;
?>

</main>

Above page-about.php PHP produces the following HTML.

<main class="l-constrained">
 	
     <article class="l-about">
     	<img src=".../me-2-300x300.png" alt="...">
	<h1>About me</h1>
	<p>...</p>
	<h2>About this site</h2>
	<p>... <a href="...">here</a>...</p>
     </article>

</main>

There’s not much CSS applied to the about page as the image and text are in the normal flow and same as previous pages constrained by max-width: 82rem. I just used some CSS on the image to keep it responsive.

.l-about > img {
    display: block;  
    max-width: 100%;
    height: auto;
    margin: 10px auto 0;
}

Contact page

Contact form is handled by Contact Form 7 plugin. After I installed the plugin, I created page-contact.php and copied the shortcode from plugin into contact page in WP admin (Pages ⟶ Contact ⟶ Edit). Page content is output by the_content() template tag.

<main class="l-constrained">
    <?php the_content();?>
</main>

The following HTML is produced by the shortcode and above PHP. There’s a lot of Contact Form 7 generated CSS classes so I left only the ones I used and removed the rest to keep it more clear in the below code snippet.

<main class="l-constrained">
 
 <h1>Contact</h1>

    <div role="form" class="wpcf7">

    <form action="/contact/#wpcf7-f2015-o1" method="post" class="wpcf7-form">

    <div class="name-email">
     <div>
       <label> Your name </label>
       <span class="your-name">
       <input type="text" name="your-name" value="" size="40" />
       </span>
     </div>

    <div>
      <label> Your email </label>
      <span class="your-email">
      <input type="email" name="your-email" value="" size="40" />
      </span>
    </div>
   </div>


   <div class="message">
      <label> Your message 
      <span class="wpcf7-form-control-wrap your-message">
      <textarea name="your-message" cols="40" rows="15" >
      </textarea>
      </span> 
      </label>
   </div>

   <p>
     <input type="submit" value="Send" class="wpcf7-submit" />
   </p>
  </form>
  </div>		
</main>

Same as previous pages the form is constrained by max-width: 82rem. There are two divs containing name and email labels. To add space between labels I wrapped containing divs in a .name-email div with display: flex and justify-content: space-between.

.name-email {
    display: flex;
    justify-content: space-between;
    margin: 0 0 1rem 0;  
}

The following declaration block adds width to divs that contain label and input.

.name-email > div {
    flex: 0 0 35rem;
}

Next we add some hight to the labels.

.name-email label {
    height: 3.5rem;
    line-height: 2.5;
}

Next two rules stretch inputs and text area to fit their containers.

.name-email input {
    width: 100%;
    height: 3.5rem;
    margin-bottom: .6rem;
}

.message textarea {
    width: 100%;
    resize: none; 
    margin-top: 0.5rem;
}

In the default markup submit button is wrapped in a p element. To center the button I made p a flexbox container, added width to the button and then centered it with left and right auto margins. There’s some other styling applied as well.

.wpcf7-form > p {
    display: flex;
    margin: 1rem 0;
}

.wpcf7-submit {
    width: 20rem;
    padding: 1rem 0;
    margin: 0 auto;
    border: 1px solid $color-2;
    border-radius: 3px;
    background: $color-3;
    transition: background-color 1s ease;
    font-size: 1.7rem;
    cursor: pointer;
}

The last declaration block takes care of how the form behaves on screens 780px and less. We change direction of the main axis with flex-flow: column so our name and email fields stack vertically and change width of containing divs to auto so they fill all the available space of the screen.

@media screen and (max-width: 780px) {
  
  .wpcf7-form {
      margin: 1rem auto 1.5rem;
  }

  .name-email {
      flex-flow: column;
  }

  .name-email > div {
      flex: 0 0 auto;
      margin-bottom: 1.5rem;
  }

}

Footer and social icons

Underscores functions.php comes with register_nav_menus to register menu locations for a theme. I registered social menu location and created new social menu in the admin area.

functions.php

// This theme uses wp_nav_menu() in one location.

register_nav_menus(
	array(
        	'primary' => esc_html__( 'header', 'my-wp-theme' ),
        	'social' => esc_html__( 'footer', 'my-wp-theme' ),
        	)
);

Then displayed the menu in the footer.php

<footer class="l-site-footer">
      <?php 
          wp_nav_menu (
          array (
            'theme_location' => 'social',
            'container'      => 'false',
            'menu_class'     => 'social',
            'link_before'    => '<span class="screen-reader-text">',
            'link_after'     => '</span>'  
            
            )
          );
      ?>
        <p>This site is hosted by <a href="https://www.bluehost.com" target="_blank">Bluehost </a></p>
  
</footer>

Font awesome icons

I also registered font awesome icons external stylesheet in functions.php.

function my_wp_theme_scripts() {

  wp_enqueue_style  ('font-awesome', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/css/all.min.css');
}

add_action( 'wp_enqueue_scripts', 'my_wp_theme_scripts' );

Then I used their CSS classes in the admin area to actually show the icons on the frontend (Appearance ⟶ Menus ⟶ Menu structure ⟶ Custom Link ⟶ CSS Classes ).

Here’s the HTML produced by the above PHP for social icons.

<footer class="l-site-footer">

<ul  class="social">
	<li  class="fab fa-github fa-lg menu-item ">
		<a target="_blank" rel="noopener" href="https://github.com/810311"><span class="screen-reader-text">github</span></a>
	</li>

	<li  class="fab fa-codepen fa-lg menu-item">
		<a href="https://codepen.io/810311/"><span class="screen-reader-text">codepen</span></a>
	</li>

	<li class="fab fa-youtube fa-lg menu-item ">
		<a href="https://www.youtube.com/channel/UC0_jTafBINnwoJDwuX9ehNg"><span class="screen-reader-text">youtube</span></a>
	</li>
</ul>

	<p>This site is hosted by <a href="https://www.bluehost.com" target="_blank">Bluehost </a></p>

</footer>

Font awesome icons not clickable

After I added font awsome CSS classes fa-github, fa-codepen and fa-youtube into CSS Classes (optional) field in the menu admin, WordPress added them to the list items as you can see in the above HTML. That makes them visible on the front end but they are not clickable because they are not part of the actual anchor. Initially I came across this problem when trying to use font awesome icons with Twenty Sixteen child theme. I posted my question on WordPress Stack Exchange and got an insteresting suggestion.

In order for this to work we need to keep font awsome CSS classes in the CSS Classes (optional) field in the menu admin. However, we can replace original content with an empty one for each list item. This would make the icons disappear on the front end.

.social li[class*='fa-']:before{ 
    content:''; 
}  

Then instead of targeting the list items we can target our anchors again with an attribute selector combined with ::before pseudo element.

.social li[class*='fa-github'] > a:before{
    content:'\f09b';
}

.social li[class*='fa-codepen'] > a:before{
    content:'\f1cb';
}


.social li[class*='fa-youtube'] > a:before{
    content:'\f167';
}

We can then add some styling.

.social li a {
    display: block;
    width: 1.7em;
    border-radius: 0.8em; 
    background: #777;
    transition: background-color 1s ease-out;
    text-decoration: none; 
    text-align: center;
    line-height: 1.7em;
    color: #fff;
} 

.social li a:hover {
    background: #555;
}