Part 4: Beginning Our First WordPress Theme


It’s time to get our hands dirty – in this tutorial we actually start on our WordPress Theme using some template tags to generate on-page content, dynamically.

We’ll also learn about WordPress’ file structure, the minimum files that a theme requires, and some Hooks to boot. This will leave us with a shell of a theme that we can build into a functional theme.

Recommended Reading

To take part, you’ll ideally have

WordPress File Structure

Open the directory where you have WordPress running, as we need to understand its structure. On the left is what’s included, and on the right are the only two things you should ever modify.

WordPress' File Structure

With the exception of wp-content and what lies within, these files form the absolute core of WordPress and should never be edited. It may cause problems, but more so, when you update WordPress, it will overwrite any changes you have made.

wp-admin: The files for the admin area are held here
wp-includes: The core of WordPress, do not touch!
wp-content: Theme and Plugin files are stored in this directory. This is the only directory you should be working in.

Minimum Required Files

For WordPress to be aware of our theme we need two files, index.php and style.css, and optionally screenshot.png

Head into wp-content/themes of your WordPress installation, and create a new folder (I’ve named mine wppro) as this is where our theme will live.

Copy the contents of the Flatstrap folder we worked with in in Templating with PHP into the newly created wppro folder to save time.


Because we’re using what’s already made the index.php already exists for use.


For WordPress to display theme information, we need to add a commented-out section to the top of our CSS file, and in the following format:

/* --------------------------------------------------------
Theme Name: WPPro/Flatstrap
Theme URI:
Description: WPPro is based on Flatstrap by littlesparkvt,
Author: YinPress
Version: 1
License: GNU
License URI:
Text Domain: 
-------------------------------------------------------- */


The screenshot should be 300*225px and a png. Here’s one I prepared earlier. Save it, and move it into the wppro folder.

Flatstrap screenshot

Log in to your wp-admin, and browse to Appearances » Themes, this is what you’ll be presented with.

we can see our theme displaying in wp-admin

Feel free to activate it, but be warned, it’s not pretty.

Introducing Template Tags

Template tags are built-in functions you can use to display the most common pieces of information like the site title, site URL, and many, many more.

All up there’s about 170 built in template tags across nine categories:

  • General tags
  • Author tags
  • Bookmark tags
  • Category tags
  • Comment tags
  • Link tags
  • Post tags
  • Post Thumbnail tags
  • Navigation Menu tags

Understanding Template Tags

Template tags typically look something like this:

<?php get_header(); ?>

This little example has all the built in code that WordPress needs to properly get and display your header.php file. It really doesn’t get any simpler than this – you don’t have to include and create extra functions. Just use the template tag get_header and you’re done.

Some template tags have additional arguments you can pass. So, in the get_header example above, this template tag accepts an argument for a name – so you can create multiple header files and call them as needed in a template.

Want a header just for the landing page that may exclude navigation menus and the like? Just create a file named header-landingpage.php and call it like this:

<?php get_header('landingpage'); ?>

Template tags give us this kind of powerful functionality right out of the box.

Find a Template Tag

With 170 plus options for template tags, it can be tough knowing which ones you should be using or whether or not you should even care to learn about most of them. I think of template tags in context of what I’m doing with a particular template page.

Remember that template tags are grouped into categories. Most are self-explanatory, but there are a few that need some explanation:

  • General tags – the bulk of your generalized functionality
  • Author tags
  • Bookmark tags – specific to Administration -> Links menu
  • Category tags
  • Comment tags
  • Link tags – for working with the current post/page or other site specific url like home page
  • Post tags
  • Post Thumbnail tags – for displaying and working with image thumbnail content and meta
  • Navigation Menu tags

So, if you are trying to customize some aspect of the comments, there is a whole series of built-in template tags just for comments. In fact, as of the publishing of this article there are 32 comment template tags. So it would be a good idea to check out the WordPress Codex site for documentation specific to what you might be trying to do.

Inevitably, someone has done what you’re trying to accomplish, but you’re going to spend a lot more time researching a solution if you don’t know what template tag you should be using.

Once you know the general category of a template tag you want to put to use, I recommend bookmarking this page so you can quickly access each one: WordPress Codex – Template Tags.

Our First Use of Template Tags

In Part 3, we used the PHP function require_once to template our index.php, and now it’s time to use WordPress Template Tags instead. They’re very straightforward, a great example of “what’s in a name.”

  • get_header
  • get_sidebar
  • get_footer

By default these tags retrieve their associated file, there’s no need to specify as we did with require_once. Open up index.php and start replacing the old

<?php require_once('header.php') ?>
<?php require_once('sidebar.php') ?>
<?php require_once('footer.php') ?>

With the new

<?php get_header() ?>
<?php get_sidebar() ?>
<?php get_footer() ?>

To test that it loads, jump back into Appearance » Theme and open a live preview. If you can see this, it’s working, but you’ll also notice it lacks CSS, we’ll get to this later.

Generating Dynamic Content

We’ll get the ball rolling with the header, starting with the title and meta content. Here’s the static content as stands.

<!DOCTYPE html>
<html lang="en">
    <meta charset="utf-8">
    <title>Getting · Flat Strap</title>

And here’s what we’re going to change it to:

<html lang="en">
    <meta charset="<?php bloginfo( 'charset' ); ?>" />
    <title><?php bloginfo( 'name' ); ?><?php wp_title(); ?></title>

This is our first use of bloginfo, in a nutshell it’s a function that when passed particular parameters will print what it’s told to the browser. Immediately we can see we’ve used parameters charset and name, which, as you might have expected, generated the charset and name of the site, as defined in WordPress settings, respectively.

Then we have wp_title, it places the page title after the site’s name. It can take a few parameters too, so read up on them if you’d like, but for our purposes it’s just fine at default.

Continuing down to the opening body tag, we need to insert the body_class function.

  <body <?php body_class(); ?> data-spy="scroll" data-target=".bs-docs-sidebar">

It applies specific functions to the body depending on the page being viewed. It’s not immediately useful, but it will be in time.

Then, still in header.php, hop down to the subhead area and replace the getting started and text below it to this

<!-- Subhead
================================================== -->
<header class="jumbotron subhead" id="overview">
  <div class="container">
    <h1><?php bloginfo( 'name' ); ?></h1>
    <p class="lead"><?php bloginfo( 'description' ); ?></p>

We’ve already used the name parameter, but description is new. Simply, it’s what you set as the tagline in Dashboard » Settings » General


We’ll skip the CSS for now and head straight to the favicons and touch icons, though the reason that both don’t work is the same. Relative to the theme everything is in the right place, but relative to WordPress it is not. So let’s take the favicon for example.

To make it work in absolute terms, this would do the trick:

<link rel="shortcut icon" href="/assets/ico/favicon.ico">
      add /wp-content/themes/wppro to the start so it becomes
<link rel="shortcut icon" href="/wp-content/themes/wppro/assets/ico/favicon.ico">

Do not do this. Things change, and by using functions we can help avoid possible problems in the future.

The right way to do it is using a function called get_template_directory and echo, a PHP language construct that prints to the browser, like so.

<!-- Le fav and touch icons -->
    <link rel="shortcut icon" href="<?php echo get_template_directory_uri(); ?>/assets/ico/favicon.ico">
    <link rel="apple-touch-icon-precomposed" sizes="144x144" href="<?php echo get_template_directory_uri(); ?>/assets/ico/apple-touch-icon-144-precomposed.png">
    <link rel="apple-touch-icon-precomposed" sizes="114x114" href="<?php echo get_template_directory_uri(); ?>/assets/ico/apple-touch-icon-114-precomposed.png">
    <link rel="apple-touch-icon-precomposed" sizes="72x72" href="<?php echo get_template_directory_uri(); ?>/assets/ico/apple-touch-icon-72-precomposed.png">
    <link rel="apple-touch-icon-precomposed" href="<?php echo get_template_directory_uri(); ?>/assets/ico/apple-touch-icon-57-precomposed.png">

This will immediately bring those back to life, and yes, you can use the same for the CSS as well, but there’s a better solution that we’ll get to.

Language Attributes

To provide the language attributes for the HTML tag, such as text direction, we’re going to use the language_attributes function, and in the same fashion as above.

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

API Hooks

Though there are a few Hooks that we’ll cover, there are two we need to implement sooner rather than later, wp_head and wp_footer. These, just like the functions earlier, may not make total sense immediately.

wp_head: must be placed immediately before the closing </head> tag
wp_footer: must be placed immediately before the closing </body> tag

These hooks provide locations for plugins to insert code where need be, and without these hooks many plugins will break. The first that springs to mind is the admin bar, as it requires both hooks to work. Go ahead and add these where advised, then activate the theme and check it out, it’s ugly because there’s no CSS, so it’s time for us to fix that.

Resurrecting CSS

It’s time to learn about Actions, Filters, and Hooks. In a nutshell, Actions add, filters remove or modify, and hooks are what we, well, hook into.

CSS files always need to be in the head of the document, thus we must hook into wp_head. Now I’ll show you exactly how you can do it and break it down nice and simple. But before you do anything delete the CSS links from header.php, including the Le Styles comment.

First up, create a new file in your theme, functions.php – This is where we’ll be writing the code to handle any custom functions we need.

Feel free to copy and paste this code into it, as I’ll explain it below.

function wppro_script_enqueuer() {
	//first we register the styles
	wp_register_style( 'bootstrap', get_stylesheet_directory_uri().'/assets/css/bootstrap.css');
	wp_register_style( 'bootstrap-responsive', get_stylesheet_directory_uri().'/assets/css/bootstrap-responsive.css');
	wp_register_style( 'docs', get_stylesheet_directory_uri().'/assets/css/docs.css');
	wp_register_style( 'prettify', get_stylesheet_directory_uri().'/assets/js/google-code-prettify/prettify.css');
    //now we enqueue them
    wp_enqueue_style( 'bootstrap' );
    wp_enqueue_style( 'bootstrap-responsive' );
    wp_enqueue_style( 'docs' );
    wp_enqueue_style( 'prettify' );
add_action( 'wp_enqueue_scripts', 'wppro_script_enqueuer');

From the top…

We create a function called wppro_script_enqueuer, and everything that happens between the curly braces is what it does.

Then there’s a load of wp_register_style, a WordPress function. It takes a number of parameters, but we are rolling with only what we need, its name, and where it can be found.

Next, we’re using wp_enqueue_style, for which we need only provide the name as a parameter as we’ve already registered it.

Finally, we use add_action. Here we instruct WordPress that when the action wp_enqueue_scripts runs, it also needs to run wppro_script_enqueuer, and whatever it returns will be added to wp_enqueue_scripts, which runs in wp_head, thus winds up in the head of the document!

Note: doing it this way means that your have control over the order your CSS files are loaded. We’ll be revisiting this function in a later tutorial when we add JavaScript into the fray, too.


You’ve probably already refreshed your page and noticed this:

The WordPress admin bar over the top of the Bootstrap fixed navbar

Which is that any browser window 980px or wider, the WordPress admin bar is on top of the Bootstrap navigation bar for which there’s really one solution to this problem. Place this in your header.php, underneath wp_head.

<?php if ( is_admin_bar_showing() ) {?>
  .navbar-fixed-top { top: 28px !important; }

This is straight forward enough, but in case you’re confused, the function is_admin_bar_showing will return either a true or false response, if it’s false nothing happens, if true it adds that CSS in the head of the document.

The WordPress admin bar is no longer hiding the bootstrap menu, nor creating a 28px gap of white

Much better.

You can go into WordPress Admin » Users » Your Profile and disable it from showing, which is sort of OK, it’ll only be a problem for logged in users with the admin bar enabled.

The End Result

Your styles should now be enqueued in the head of the document, thus, making the layout come back to life.

CSS restored by out function

Winding Up

We’ve covered quite an extraordinary amount of WordPress here, if you don’t remember or understand everything we’ve done, that’s OK, it’s something that will make more sense the more you do it, so practice, practice, practice!

Table of Contents

Share on Facebook Back to Top


  • Thank you for putting this tutorial together – it’s exactly what I need to get a better understanding of how to work with WordPress. Having trouble with the style.css file though. Are you combining all of the original css files from ‘getting-started.html’? I can’t see anywhere that we’ve made a separate style.css to place into the theme folder.

  • I have put in both hooks (wp_head and wp_footer) and the “wppro_script_enqueuer” function in the functions.php, but I must have missed a step because I have no styles showing up. I would love any help. Also I love the way this tutorial is written in bite size chunks. Thanks

    • Hey Jason,

      Strange problem, I bet it’s something super easy to fix though. If you can zip the theme up as it stands and send it to or put it up on Github I’ll take a look at it for you.

      Also, I started rewriting this series on Friday (currently at part 8) and have been keeping it on Github myself, you can view it here if you wanted to see where it’s at. I’ve also kept a zip of the theme per end of each part on there.

    • Hi Jason,

      Did you write your wp_head and wp_footer in php tags like so?


      • Oops the syntax was removed from my last comment. Does BB Code work?….

        <?php wp_head(); ?>
        <?php wp_footer(); ?>
        • Thanks for the help. I emailed my files over to Aaron Osteraas and he pointed out that I had instead of (I did this for sidebar, header, footer). I did a find/replace on “require_once” in , not realizing there was more that had to be changed. Thanks again, and now I will continue on with the rest of the tutorial with my bruised ego.

  • Thanks for the information love it.

  • Awesome….indeed a good job…. Keep it Aaron…

  • The tutorial is awesome. I’m developing a custom theme for my company website. Its a very big task but with this tutorial it makes it a heck of a lot easier. This is the first tutorial I found that actually breaks down the important things, its easy to follow and understand. I’m looking forward to getting into the advanced version.

    P.S I don’t know if anyone else notice this, but as I was following the tutorial the parts began to get mixed (link wise) and it was confusing to remember where I had left off when I would revisit a post. I recommend using Series by Justin Tadlock, its a great plugin, and it really helps with writing and keeping series organized. You’ll love it 🙂

  • Hi, I Need Help..
    Not sure about this, but is look like i miss a step..
    iam not understand from section API HOOKS until the end..
    Actually about wp_head and wp_footer too

    It gonna helps if i know what the exactly correct header.php based this tutorial..

    ps. iam new at wordpress

    thank you

    • Hey Nurul!

      The API hook wp_head has to go in the header.php file, somewhere before the </head> tag. The API hook wp_footer needs to go in footer.php somewhere before the </body> tag.

      You can check out our theme’s header.php and footer.php to see where they go. Don’t copy the entire code from this into your files, only what you need, as the rest will be covered in future steps.

      I hope this helps, but please let me know if you need any more assistance and I will be here to help 🙂

      • thank for your help Aaron, but i cant found wp_head abd wp_footer at your header.php and footer.php

        i found it in this Codex

  • You should add a ?> after add_action( 'wp_enqueue_scripts', 'wppro_script_enqueuer'); line. If you don’t, will get an Syntax error.

    • Adding that shouldn’t be necessary for this part actually, what specific error is it generating?

  • Just awesome, keep it up…

  • Hi, Aaron,

    I really appreciate and love your tutorials! However, while following your tutorial above step-by-step, I think I’ve missed something that now my top navigation bar (the one with WordPress logo and blog name) doesn’t show up. I’ve already added hook just before the tag. Help me, please…

    • Hi, Aaron,

      False alarm! I’m very sorry for the above comment. Apparently it was my fault. I put the wp_head hook before the end of body tag (which I was supposed to put wp_footer hook). You may delete my comment if you wish as it was none of your fault. My bad… 🙁

      • All good 🙂 Let me know if you get stuck and need a hand!

Speak up! Let us know what you think.