How To Create A Simple WordPress Theme From Scratch

The benefit of making your own custom WordPress theme from scratch is that you have full control over every aspect of your website, and you’ll learn a lot about WordPress along the way.  Why bother making your own WordPress theme from scratch when there are so many great themes out there to choose from?   Pre-made themes are great and a huge time-saver.  But they are typically loaded with a bunch of features and plugins that you may not want or need.

So we wanted a simple, lightweight theme that was responsive (we use css grids) and did not contain a lot of markup, deeply nested divs, or unnecessary class assignments.

WordPress only needs two files to recognize your custom theme:

  • style.css
  • index.php

You’ll add the following code to the top of your style.css file, which registers your theme.  (You can focus on defining styles later)

/*
Theme Name: My Custom Theme
Author: Me!
Description: Custom WordPress Theme
Version: 0.0.1
*/

However, these two files won’t get you very far.

To have a functioning website, you’ll need the following files

  • style.css
  • header.php
  • index.php (serves as home page)
  • single.php (provides single post display)
  • search.php (display search results and category listings)
  • footer.php
  • 404.php (in case all else fails)

Let’s say you want your html page layout to look like this:

<!doctype html>
<html lang="en-US">
<head>
  My Header Content
</head>

<body>
<div id="page">

  <div id="header">
    <div class="header">
      My Header Content
    </div>
  </div>

  <div id="content" class="content">
    <div id="main-content">
      My Website Content
    </div>
    <div id="main-content"> 
      My Sidebar Content 
    </div>
  </div>
  
  <div id="footer">
    <div class="footer">
      My Footer Content
    </div>
  </div>

</div>
</body>
</html>

Everything above the words “My Website Content” would be placed in your header.php file, along with a few WordPress variables to make it work.  Using this example, your header file would look like this:

header.php

<!doctype html>
<html <?php language_attributes(); ?>>
<head>
  <meta charset="<?php bloginfo( 'charset' ); ?>">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="profile" href="http://gmpg.org/xfn/11">
  <title><?php wp_title(); ?></title>
  <?php wp_head(); ?>
</head>

<body <?php body_class(); ?>>
<div id="page">

  <div id="header">
    <div class="header">
      <div class="logo">
        <h1 class="site-title"><a href="<?php echo esc_url( home_url( '/' ) ); ?>" rel="home"><?php bloginfo( 'name' ); ?></a></h1>
      </div>
    </div>
  </div>

  <div id="content" class="content">

Everything below the words “My Website Content” would be placed in your footer.php file, along with a few WordPress variables to make it work.  Using this example, your footer file would look like this:

footer.php

  </div><!-- end #content -->

  <footer>
  <div id="footer">
    <div class="footer">
      Copyright &copy; <?php echo date("Y") ?> <a href="<?php echo get_option('home'); ?>"><?php bloginfo('name'); ?></a>
    </div>
  </div>
  </footer>

</div><!-- end #page -->
<?php wp_footer(); ?>
</body>
</html>

Our sidebar.php can hold any number of things.  Here, we’ll just add the search form for now.

sidebar.php

<div id="sidebar">
  <div class="sidebar">
    <?php get_search_form(); ?>
  </div>
</div>

Now we need to create an index.php file to pull it all together.  It will call our head.php file and footer.php by using get_header(); and get_footer();  

Because it is our default home page, in this example we’ll use it to display recent posts.  We’ll also add our sidebar using get_sidebar().

index.php

<?php
get_header();
?>

  <div id="main-content">
    <div class="grid">
      <?php
      // Define number of posts to be shown and from which category
      $args = array( 'numberposts' => 10, 'category_name' => 'my_category' );
      $posts = get_posts($args);
      foreach($posts as $post) {
        setup_postdata( $post );
        // Display the title
        echo '<div class="teaser"><h1><a href="' . get_permalink() . '">' . get_the_title() . '</a></h1>';
        // Display an excerpt of text
        the_excerpt();
        // Display an edit link to admins
        edit_post_link(__('Edit This'));
        echo '</div>';
      } ?>

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


<?php
get_footer();

We’ve called posts using get_posts() and filtered to a specific category as well as requested 10 posts be displayed on our page.  We’ve also formatted each post to display an excerpt of text, and added an edit button for admin users (for convenience).

single.php

The single.php is used to format a single posts.  So if someone clicks on one of the posts listed on our index.php page, that individual post is displayed using single.php.

<?php
get_header();
?>

  <div id="main-content">

    <?php
    while ( have_posts() ) : the_post();
        echo '<div class="post"><h1><a href="' . get_permalink() . '">' . get_the_title() . '</a></h1>';
        the_content();
 	echo '</div>';
    endwhile;
    ?>
  </div>
  <?php get_sidebar(); ?>

<?php
get_footer();

404.php

404 pages are a good method for presenting site visitors with other options should a page they’re looking for go missing (moved/renamed/deleted).    Without a 404 page, they would see a server error message and likely leave your site rather than search for what they’re looking for.

Here, we’ll apologize and offer up some other posts they might be interested in.

<?php
get_header();
?>

  <div id="main-content">
    <div class="404">
      <h1>Oops!  We couldn't find that page</h1>
      <div>It looks like the page you were looking for may have been moved, renamed, or deleted.  Here's a list of some recent articles that may help, or try using our search form.  Sorry!</div>
      <?php
      $args = array( 'numberposts' => 10, 'category_name' => 'category' );
      $posts = get_posts($args);
      foreach($posts as $post) {
        setup_postdata( $post );
        echo '<div class="post"><h5><a href="' . get_permalink() . '">' . get_the_title() . '</a></h5>';
        echo '</div>';
      } ?>

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


<?php
get_footer();

search.php

You will need a way to display search results if a visitor uses your search form.

<?php
get_header();
?>

  <div id="main-content">
    <div class="search-results">
      <h1 >Searching for <?php the_search_query(); ?></h1>
      <?php
      if ( have_posts() ) {
                while ( have_posts() ) : the_post();
          foreach($posts as $post) {
            setup_postdata( $post );
            echo '<div><h1><a href="' . get_permalink() . '">' . get_the_title() . '</a></h1>';
            the_excerpt();
            edit_post_link(__('Edit This'));
            echo '</div>';
            }
                endwhile;
            } else {
            	$searchTerm = get_search_query();
                echo '<div>Sorry, we weren\'t able to find any articles matching "' . $searchTerm . '"</div>';
            }
            ?>
    </div>
  </div>
  <?php get_sidebar(); ?>


<?php
get_footer();

 

style.css

Here’s a basic stylesheet to use as a starting point

/*
Theme Name: My Theme Name
Author: Me!
Description: Custom WordPress Theme
Version: 0.0.1
*/

/* Remove whitespace from top */
html {
    margin: -22px !important;
}

body {
    font-size: 1em;
    font-family: sans-serif;
    line-height: 1.5;
    color: #313131;
    margin: 0px;
    padding: 0px;
  background-color: #ffffff;    
}

a {
  color: green;
  text-decoration: none;
}

div {
    position: relative;
}

div#page {
    width: 100%;
    margin: 0 auto;
}

#header,
#footer {
  width: 100%;
  background-color: #e0e0e0;
  padding: 40px 0;
}

.header,
#content,
.footer {
    width: 85%;
    margin: 0 auto;
    max-width: 1264px;
}

#main-content {
    min-height: 600px;
}

/* Responsive content columns */
/* 1st col gets 2 frames, 2nd gets 1 frame
/* https://medium.freecodecamp.org/how-to-make-your-html-responsive-by-adding-a-single-line-of-css-2a62de81e431
*/
.container {
    display: grid;
    grid-template-columns: 2.25fr .75fr;
}

/* Responsive content grids */
/* Automatically fits divs within space */
.grid {
    display: grid;
    grid-gap: 40px;
    grid-template-columns: repeat(auto-fit, minmax(240px, 350px));
    /* grid-template-rows: repeat(2, 100px); */
}

That’s it!  You’ll have a lightweight, responsive starter theme using these template files.  You can extend your template further by using functions such as get_template_parts() and fine-tuning pages by category or tag, but we just want simplicity for now.

Again, this is just a very basic starter theme.  If you’ll be using custom taxonomies, articles images, or custom fields, etc, you will need to build a slightly more advanced theme.  Stay tuned for a complete guide on building an advanced theme.

Available for Amazon Prime