custom wordpress header file layout template

How To Create Custom WordPress Header: Theme Development

Many WordPress themes come with a built-in header that sits at the top of each page. You may need to customize it to add important links, social icons, site search, or create your own custom WordPress header layout for your theme.

In this article, we’ll show you how to customize your WordPress header and even create a fully custom header for your entire site or specific pages.

Create the custom WordPress header layout

For creating the custom header template for your WordPress website you have to create its hooks, CSS styles, and scripts.

custom wordpress header

Your structure should look like the picture in your them. Open your theme and create missing files with the structure.

If functions.php, index.php, and style.css is missing, create them based on our tutorial here.

functions.php file

Open your functions.php file and add the following codes if not exists.

include('inc/hs_menu_walker.php');

if (!function_exists('hs_sample_setup_header')) :
    function hs_sample_setup_header()
    {
        register_nav_menus(
            array(
                'main-menu' => esc_html__('Main Menu', 'hs-sample'),
                'mobile-menu' => esc_html__('Mobile Menu', 'hs-sample'),
            )
        );

        $args = array(
            'default-text-color' => '000',
            'width'              => 1000,
            'height'             => 250,
            'flex-width'         => true,
            'flex-height'        => true,
        );
        add_theme_support('custom-header', $args);

        add_theme_support('custom-logo', array(
            'height'               => 100,
            'width'                => 400,
            'flex-height'          => true,
            'flex-width'           => true,
            'unlink-homepage-logo' => true,
        ));
    }
    add_action('after_setup_theme', 'hs_sample_setup_header');
endif;

if (!function_exists('hs_sample_enqueue_header')) {
    function hs_sample_enqueue_header()
    {
        wp_enqueue_style('hss-header', esc_url(get_template_directory_uri() . '/assets/css/header.css'), array(), '1.0.0');

        wp_enqueue_script('hss-header-script', esc_url(get_template_directory_uri() . '/assets/js/header.js'), ['jquery'], '1.0.0', true);
    }
    add_action('wp_enqueue_scripts', 'hs_sample_enqueue_header');
}

The first function defines the header elements like menu locations, website logo, and header background image. And the second function enqueued header style file and its script.

In this example, we need some general CSS codes from the style.css file which we will explain in this article. Then add the below WordPress hook if not exists.

if (!function_exists('hs_sample_enqueue_theme')) {
    function hs_sample_enqueue_theme()
    {
        wp_enqueue_style('hss-style', get_stylesheet_uri());
    }
    add_action('wp_enqueue_scripts', 'hs_sample_enqueue_theme');
}

header.php

Your website header is the top section of every page on your WordPress website, and probably the first thing your visitors will see.

This file is a reserved file name in WordPress to develop and contain header content of the website like head tags, and meta tags and etc. header file contains every content that will include on every page of the website like the main menu, website logo, etc.

Open the header.php file and add the following codes.

<!doctype html>
<html <?php language_attributes(); ?>>

<head>
    <meta charset="<?php bloginfo('charset'); ?>" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <link rel="profile" href="https://gmpg.org/xfn/11">
    <link rel="pingback" href="<?php bloginfo('pingback_url'); ?>">
    <?php wp_head();  ?>
</head>

<body <?php body_class(); ?>>
    <?php wp_body_open(); ?>
    <div class='hss-site'>
        <a class='hss-skip-link screen-reader-text' href='#content'>
            <?php esc_html_e('Go To Content', 'hs-sample');
            ?>
        </a>
        <button class='hss-to-top-button' onclick='window.scrollTo({top: 0, behavior: "smooth"});'>
            <span class="dashicons dashicons-arrow-up-alt2"></span>
        </button>
        <?php
        $header_image_url = header_image();
        ?>
        <header class="hss-header hs-container" <?php echo !empty($header_image_url) ? 'style="background-image:url(' . esc_url($header_image_url) . ')"' : ''; ?>>
            <div class="hs-row">
                <div class="hs-col-2 hs-col-m-12 hs-col-t-6 hs-order-t-1">
                    <div class='hss-logo'>
                        <?php
                        if (has_custom_logo()) :
                            the_custom_logo();
                        else :
                            $blog_info    = get_bloginfo('name');
                            $blog_description = get_bloginfo('description');
                            $show_title   = (true === display_header_text());
                            $header_class = $show_title ? 'site-title' : 'screen-reader-text';
                        ?>
                            <?php if ($blog_info) : ?>
                                <?php if (is_front_page() && !is_paged()) : ?>
                                    <h1 class="<?php echo esc_attr($header_class); ?>"><?php echo esc_html($blog_info);?></h1>
                                    <span class="site-description"><?php echo esc_html($blog_description); ?></span>
                                <?php elseif (is_front_page() && !is_home()) : ?>
                                    <h1 class="<?php echo esc_attr($header_class); ?>">
                                        <a href="<?php echo esc_url(home_url('/')); ?>">
                                            <?php echo esc_html($blog_info); ?>
                                        </a>
                                    </h1>
                                    <span class="site-description"><?php echo esc_html($blog_description); ?></span>
                                <?php else : ?>
                                    <h1 class="<?php echo esc_attr($header_class); ?>">
                                        <a href="<?php echo esc_url(home_url('/')); ?>">
                                            <?php echo esc_html($blog_info); ?>
                                        </a>
                                    </h1>
                                    <span class="site-description"><?php echo esc_html($blog_description); ?></span>
                                <?php endif; ?>
                            <?php endif; ?>
                        <?php endif; ?>
                    </div>
                </div>
                <div class="hs-col-7 hs-col-m-6 hs-col-t-12 hs-order-t-3">
                    <div class="hss-main-menu">
                        <?php if(wp_is_mobile()): ?>
                            <?php if (has_nav_menu('mobile-menu')) : ?>
                                <nav class="navbar">
                                    <button class="navbar-toggler" type="button" onclick="hss_open_menu(this)">
                                        <span class="dashicons dashicons-menu-alt3"></span>
                                    </button>
                                    <div id="bscollapse" class="navbar-box">
                                        <button class="menu-close" onclick="hss_close_menu(this)">
                                            <span class="dashicons dashicons-no-alt"></span>
                                        </button>
                                        <?php
                                        wp_nav_menu(array(
                                            'theme_location'    => 'mobile-menu',
                                            'container'         => 'div',
                                            'menu_class'        => 'nav navbar-nav',
                                            'walker' => new HS_Menu_Walker()
                                        ));
                                        ?>
                                    </div>
                                </nav>
                            <?php endif; ?>
                        <?php else: ?>
                            <?php if (has_nav_menu('main-menu')) : ?>
                                <nav class="navbar">
                                    <div id="bscollapse" class="navbar-box">
                                        <?php
                                        wp_nav_menu(array(
                                            'theme_location'    => 'main-menu',
                                            'container'         => 'div',
                                            'menu_class'        => 'nav navbar-nav',
                                            'walker' => new HS_Menu_Walker()
                                        ));
                                        ?>
                                    </div>
                                </nav>
                            <?php endif; ?>
                        <?php endif; ?>
                    </div>
                </div>
                <div class="hs-col-3 hs-col-m-6 hs-col-t-6 hs-justify-content-m-end hs-order-t-2">
                    <?php get_search_form(); ?>
                </div>
            </div>
        </header>

The head tag section contains the meta tags, title, and so on to add information and enqueued file to WordPress.

Just remember that the wp_head() function must be before the </head> tag. This function controls the codes that will be added to the header by WordPress.

The wp_body_open() function must be added after the <body> tag too.

Skip link

Every standard theme should have the skip link for more accessibility to the content of the page. In this example, if the user clicks on the skip link, the user will jump to the section that has the “content” id.

Back to top button

If you navigate the websites you will see a button usually at the bottom of the page that if you click it you will back to the top of the page.

This example includes a button for this purpose.

Header background image

Every standard theme in WordPress must implement this feature. If you go to the customizer you will see the header image section to customize and add an image to the header.

Website logo

If you add the custom WordPress logo feature in functions.php you can see the logo section in customize the WordPress. In this example, if you add the logo in customization, your code will display the logo, if you not, the code will display the website title and description.

To define the title and description, go to “Settings” in Dashboard.

Menu

In the functions.php file, we added two menu locations (Main Menu and Mobile Menu). In this example, we separated the desktop and mobile menu items.

With the wp_is_mobile() function you can detect the user device. Unfortunately, there is no straightforward function for detecting the tablet mode.

This example uses a menu walker (HS_Menu_Walker class) to display the menu items. Using this class is optional, but if you want to arrange menu items for your purposes you have to use the walker class.

Open the hs_menu_walker.php file and add the following codes.

class HS_Menu_Walker extends Walker
{
    var $db_fields = [
        'parent' => 'menu_item_parent',
        'id' => 'db_id',
    ];

    public function start_lvl( &$output, $depth = 0, $args = null ) {
		if ( isset( $args->item_spacing ) && 'discard' === $args->item_spacing ) {
			$t = '';
			$n = '';
		} else {
			$t = "\t";
			$n = "\n";
		}
		$indent = str_repeat( $t, $depth );

		// Default class.
		$classes = array( 'sub-menu' );

		/**
		 * Filters the CSS class(es) applied to a menu list element.
		 *
		 * @since 4.8.0
		 *
		 * @param string[] $classes Array of the CSS classes that are applied to the menu `<ul>` element.
		 * @param stdClass $args    An object of `wp_nav_menu()` arguments.
		 * @param int      $depth   Depth of menu item. Used for padding.
		 */
		$class_names = implode( ' ', apply_filters( 'nav_menu_submenu_css_class', $classes, $args, $depth ) );
		$class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';

		$output .= "{$n}{$indent}<ul$class_names>{$n}";
	}

	public function end_lvl( &$output, $depth = 0, $args = null ) {
		if ( isset( $args->item_spacing ) && 'discard' === $args->item_spacing ) {
			$t = '';
			$n = '';
		} else {
			$t = "\t";
			$n = "\n";
		}
		$indent  = str_repeat( $t, $depth );
		$output .= "$indent</ul>{$n}";
	}

	public function start_el( &$output, $data_object, $depth = 0, $args = null, $current_object_id = 0 ) {
		// Restores the more descriptive, specific name for use within this method.
		$menu_item = $data_object;

		if ( isset( $args->item_spacing ) && 'discard' === $args->item_spacing ) {
			$t = '';
			$n = '';
		} else {
			$t = "\t";
			$n = "\n";
		}
		$indent = ( $depth ) ? str_repeat( $t, $depth ) : '';

		$classes   = empty( $menu_item->classes ) ? array() : (array) $menu_item->classes;
		$classes[] = 'menu-item-' . $menu_item->ID;

		$args = apply_filters( 'nav_menu_item_args', $args, $menu_item, $depth );

		$class_names = implode( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $menu_item, $args, $depth ) );
		$class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';

		$id = apply_filters( 'nav_menu_item_id', 'menu-item-' . $menu_item->ID, $menu_item, $args, $depth );
		$id = $id ? ' id="' . esc_attr( $id ) . '"' : '';

		$output .= $indent . '<li' . $id . $class_names . '>';

		$atts           = array();
		$atts['title']  = ! empty( $menu_item->attr_title ) ? $menu_item->attr_title : '';
		$atts['target'] = ! empty( $menu_item->target ) ? $menu_item->target : '';
		if ( '_blank' === $menu_item->target && empty( $menu_item->xfn ) ) {
			$atts['rel'] = 'noopener';
		} else {
			$atts['rel'] = $menu_item->xfn;
		}
		$atts['href']         = ! empty( $menu_item->url ) ? $menu_item->url : '';
		$atts['aria-current'] = $menu_item->current ? 'page' : '';

		$atts = apply_filters( 'nav_menu_link_attributes', $atts, $menu_item, $args, $depth );

		$attributes = '';
		foreach ( $atts as $attr => $value ) {
			if ( is_scalar( $value ) && '' !== $value && false !== $value ) {
				$value       = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value );
				$attributes .= ' ' . $attr . '="' . $value . '"';
			}
		}

		$title = apply_filters( 'the_title', $menu_item->title, $menu_item->ID );

		$title = apply_filters( 'nav_menu_item_title', $title, $menu_item, $args, $depth );

		$item_output  = $args->before;
		$item_output .= '<a' . $attributes . '>';
		$item_output .= '<span class="menu-text">'.$args->link_before . $title . $args->link_after.'</span>';
		if(wp_is_mobile()){
			$item_output .= in_array('menu-item-has-children',$classes) ? '<span class="dashicons dashicons-arrow-down"></span>':'';
		}
		else{
			if($depth == 0){
				$item_output .= in_array('menu-item-has-children',$classes) ? '<span class="dashicons dashicons-arrow-down"></span>':'';
			}
			else{
				$item_output .= in_array('menu-item-has-children',$classes) ? '<span class="dashicons dashicons-arrow-right"></span>':'';
			}
		}
		$item_output .= '</a>';
		$item_output .= $args->after;

		$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $menu_item, $depth, $args );
	}

	/**
	 * Ends the element output, if needed.
	 *
	 * @since 3.0.0
	 * @since 5.9.0 Renamed `$item` to `$data_object` to match parent class for PHP 8 named parameter support.
	 *
	 * @see Walker::end_el()
	 *
	 * @param string   $output      Used to append additional content (passed by reference).
	 * @param WP_Post  $data_object Menu item data object. Not used.
	 * @param int      $depth       Depth of page. Not Used.
	 * @param stdClass $args        An object of wp_nav_menu() arguments.
	 */
	public function end_el( &$output, $data_object, $depth = 0, $args = null ) {
		if ( isset( $args->item_spacing ) && 'discard' === $args->item_spacing ) {
			$t = '';
			$n = '';
		} else {
			$t = "\t";
			$n = "\n";
		}
		$output .= "</li>{$n}";
	}
}

Also in the mobile menu, we added two functions in onclick event that they are JS functions and we will explain them in the following sections of this article.

Custom search form in WordPress header

The last section of the header is the search form. In structure, we have the searchform.php file in the root. Open it and add the below codes.

<?php
if (!defined('ABSPATH')) {
    exit;
}
?>
<form role="search" method="get" class="hss-search-form" action="<?php echo esc_url(home_url('/')); ?>">
    <input type="search" placeholder="Search" class="search-field" value="<?php echo esc_attr(get_search_query()); ?>" name="s" />
    <button type="submit" class="search-submit"><span class="dashicons dashicons-search"></span></button>
</form>

In this file, you can create a custom search form for your WordPress website. If you call the get_search_form() function in your code, the codes of this file will be executed.

The custom header style

We created the header of our theme PHP code. But we have to add some CSS style and JS code to make it beautiful and responsive.

In this example, we used some class names with HTML tags. Now let’s add their styles.

style.css file

In this example, this file contains the general style of the website. But we will add the CSS codes that are related to our header style.

Open the file and add the following codes at the end of the file if they do not exist.

*,
::after,
::before {
    box-sizing: border-box;
}

body {
    margin: 0;
}

.hs-container {
    width: 1170px;
    margin: 0 auto;
}

.hs-row {
    display: flex;
    flex-wrap: wrap;
}

.hs-col-1 {
    width: 8.333333%;
}

.hs-col-2 {
    width: 16.666666%;
}

.hs-col-3 {
    width: 25%;
}

.hs-col-7 {
    width: 58.333333%;
}

@media (max-width: 1170px) {
    .hs-container {
        width: 100%;
    }
}

@media (min-width: 767px) and (max-width: 991px) {
    .hs-order-t-1{
        order: 1;
    }

    .hs-order-t-2{
        order: 2;
    }

    .hs-order-t-3{
        order: 3;
    }

    .hs-col-t-6{
        width: 50% !important;
    }

    .hs-col-t-12{
        width: 100% !important;
    }
}

@media (max-width: 767px) {
    .hs-container {
        width: 100%;
    }

    .hs-col-m-12 {
        width: 100% !important;
    }

    .hs-col-m-6 {
        width: 50% !important;
    }

    .hs-justify-content-m-end {
        display: flex;
        justify-content: end;
    }
}

.hss-site {
    position: relative;
}

If you are familiar with Bootstrap you will find them look like the Bootstrap classes.

Read More: Create Bootstrap WordPress theme with v4 & 5

header.css file

This file contains the style of our header CSS codes.

.hss-to-top-button {
    position: fixed;
    right: 15px;
    bottom: 15px;
    padding: 10px;
    border-radius: 3px;
    -webkit-border-radius: 3px;
    border: 1px solid #00ABB3;
    background: #fff;
    color: #00ABB3;
    cursor: pointer;
}

.hss-to-top-button:hover {
    background: #00ABB3;
    color: #fff;
}

/*** header itself style ***/
.hss-header {
    padding: 10px 0;
}

/*** site title and description style ***/
.hss-header .site-title,
.hss-header .site-title a {
    font-size: 28px;
    margin: 0;
    color: #00ABB3;
    text-decoration: none;
}

.hss-header .site-title a:hover {
    color: #3C4048;
}

.hss-header .site-description {
    font-size: 14px;
    color: #3C4048;
    margin-top: 5px;
    display: inline-block;
}

/*** header menu style ***/
.hss-header .hs-row {
    align-items: center;
}

.hss-header nav ul {
    list-style: none;
    display: flex;
    flex-wrap: wrap;
    padding: 0;
    margin: 0;
}

.hss-header nav ul li:not(.sub-menu li) {
    width: auto;
    position: relative;
}

.hss-header nav ul a {
    text-decoration: none;
    color: #3C4048;
    padding: 5px 10px 5px 0;
    font-size: 18px;
    font-weight: 700;
    display: flex;
    align-items: center;
}

.hss-header nav ul a:hover {
    color: #00ABB3;
}

.hss-header nav .sub-menu {
    position: absolute;
    left: 0;
    min-width: 250px;
    background: #fff;
    box-shadow: #3C4048 2px 2px 10px -4px;
    visibility: hidden;
    opacity: 0;
    margin-left: 1px;
}

.hss-header nav .sub-menu li {
    width: 100%;
    position: relative;
}

.hss-header nav .sub-menu a {
    background: #fff;
    color: #3C4048;
    font-size: 14px;
    font-weight: 400;
    padding: 0;
}

.hss-header nav .sub-menu a:hover {
    background: #00ABB3;
    color: #fff;
}

.hss-header nav a>span.menu-text {
    padding: 15px 10px;
    width: 100%;
}

.hss-header nav .sub-menu .menu-item-has-children a>span.dashicons {
    padding-right: 10px;
}

.hss-header nav .menu-item-has-children:hover>.sub-menu {
    visibility: visible;
    opacity: 1;
}

.hss-header nav .sub-menu .sub-menu {
    left: 100%;
    top: 0;
}

/*** search box ***/
.hss-header .hss-search-form {
    display: flex;
    align-items: center;
    justify-content: end;
}

.hss-header .hss-search-form .search-field {
    padding: 10px;
    outline: none;
    border: none;
    border-bottom: 1px solid #3C4048;
    color: #3C4048;
}

.hss-header .hss-search-form .search-submit {
    padding: 8px;
    border: none;
    border-bottom: 1px solid #3C4048;
    background: transparent;
    cursor: pointer;
    color: #3C4048;
}

.hss-header .hss-search-form .search-submit:hover {
    color: #00ABB3;
}

/*** mobile mode style ***/
@media (min-width: 992px) and (max-width: 1200px) {
    .hss-header {
        padding: 10px;
    }
}
@media (min-width: 768px) and (max-width: 991px) {
    .hss-header {
        padding: 10px;
    }

    .hss-header .navbar-toggler,
    .hss-header .menu-close {
        display: none;
    }

    .hss-header nav a > span.menu-text{
        padding: 0;
    }

    .hss-header nav ul a{
        padding: 15px 15px 15px 0;
    }

    .hss-header nav .sub-menu a{
        padding: 10px;
    }

    .hss-header nav .sub-menu .menu-item-has-children a > span.dashicons::before{
        content: "\f139";
    }

    /* should be in the end */
    .sub-menu-display {
        visibility: visible !important;
        opacity: 1 !important;
    }
}

@media (max-width: 767px) {
    .hss-header {
        padding: 10px;
    }

    .hss-header .navbar-box {
        display: none;
        position: absolute;
        background: #fff;
        width: 100vw;
        height: 100vh;
        top: 0;
        left: 0;
        overflow-y: auto;
    }

    .hss-header .navbar-toggler,
    .hss-header .menu-close {
        padding: 10px;
        background: #fff;
        cursor: pointer;
        border: 1px solid #3C4048;
        color: #3C4048;
        margin-top: 15px;
    }

    .hss-header .menu-close {
        margin-left: 10px;
    }

    .hss-header nav ul li {
        border-bottom: 1px solid #EAEAEA;
    }

    .hss-header nav ul li:not(.sub-menu li) {
        width: 100%;
    }

    .hss-header nav ul a {
        padding: 0;
    }

    .hss-header nav .menu-item-has-children a>span.dashicons,
    .hss-header nav .sub-menu .menu-item-has-children a>span.dashicons {
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 25px;
        border-left: 1px solid #EAEAEA;
    }

    .hss-header nav .sub-menu {
        min-width: 100%;
        background: #fff;
        box-shadow: none;
        margin-left: 0;
    }

    /* should be in the end */
    .sub-menu-display {
        position: relative !important;
        left: 0 !important;
        visibility: visible !important;
        opacity: 1 !important;
    }

    .navbar-box.collapsed {
        display: block;
    }
}

This file defines the codes to make our header beautiful and responsive. Making the header menu responsive needs JavaScript to function. In desktop mode, we have :hover to display sub-menus but in mobile mode, we don’t have the feature, we have only the click event.

In CSS we don’t have the click event then we have to use both to make the header menu responsive. Open the header.js file and add the following code.

function hss_close_menu(node) {
    node.parentNode.classList.remove("collapsed");
  }
  
  function hss_open_menu(e) {
    var menu = e.nextElementSibling;
    menu.classList.add("collapsed");
  }
  
  jQuery(document).ready(function () {
    if (window.innerWidth < 992) {
      jQuery(document).on("click", function () {
        var nodes = document.querySelectorAll(".hss-main-menu .sub-menu-display");
        nodes.forEach(function (item) {
          item.classList.remove("sub-menu-display");
        });
      });
  
      document
        .querySelector(".hss-main-menu")
        .addEventListener("click", function (event) {
          event.stopPropagation();
        });
      jQuery(".hss-main-menu .navbar-nav li.menu-item-has-children > a").on(
        "click",
        function (e) {
          e.preventDefault();
          var node = this.nextElementSibling;
          if (node.className == "sub-menu") {
            node.classList.add("sub-menu-display");
          } else {
            node.classList.remove("sub-menu-display");
          }
        }
      );
    }
  });

The hss_open_menu() function adds a class name to the menu that displays non-visible menus. The hss_close_menu() removes the class to back the menu to hidden.

In the functions.php file, we enqueued this file after the jQuery, which means we can use jQuery codes in this file.

After the functions, we use document ready and window width to execute its codes only in mobile mode if the document was ready.

If the user clicks on a menu item that has a submenu, the click will be emitted and the submenu will be displayed. If the submenu is hidden this will be visible and if it is visible it will be hidden (the submenu will be opened and closed).

In tablet mode, the user will see the menu like in desktop mode but still, there is no :hover feature in the tablet. This code defines some codes to close the menu if the user clicks outside of the menu.

Download

You can download the whole project from GitHub.

Shopping Cart