Macbook Pro Showing Text

A theme is a collection of code and resources that help render our content to a web page. In this article we will dive into the essentials of developing a theme.

Two types of themes

Themes were introduced with WordPress version 1.5, back in 2005, making the templating system more robust and flexible. Theme authors could implement different PHP templates for different parts of the site, and template parts made it easy to reuse common code.

Fast forward to 2022, version 5.9 introduced a new theme system. Templates no longer had to be PHP files but rather HTML, and with the introduction of blocks, full site editing was eventually possible. Since the old system is still supported, we refer to themes developed the old way as classic themes, as opposed to the new block themes.

Whatever way you choose to develop your theme, you will have to include at least two files: the main stylesheet and the default template.

The main stylesheet

When WordPress needs to discover the installed themes, it scans all folders in wp-content/themes for files named style.css that contain a special header. If it finds a file like that, the folder is considered the root of a theme. The header is a comment block containing at least a Theme Name field, as illustrated below:

/*
Theme Name: Tutorial
*/
CSS

There are many other fields that you can add to the header. Below is a list of all other possible fields:

  • Description: a desctiption of the theme
  • Version: the theme’s version
  • Theme URI: a URI that leads to more information about the theme
  • Author: the theme’s author
  • Author URI: a URI that leads to more information about the author
  • License: the license under which the theme is released
  • License URI: a URI to the full license text
  • Tested up to: the last WordPress version the theme was tested against
  • Requires at least: the minimum WordPress version the theme requires to function
  • Requires PHP: the minimum PHP version the theme requires to function
  • Text Domain: the string used for the textdomain for translations
  • Domain Path: a path relative to the theme’s root that contains translations, if it’s different than languages
  • Tags: tags designating the theme’s features, separated with commas, preferably from this list

There is also a Template field, but you should only include that in child themes.

The default template

Before we begin we have to note that, in classic themes, templates are PHP files located in the theme’s root folder. On the other hand, in block themes, templates are HTML files located in a templates subfolder.

Now, when WordPress has to decide what template is most suitable for the content at hand, it utilizes a predefined template hierarchy. The ultimate fallback in that hierarchy is the default template, index.php for classic themes or templates/index.html for block themes. That’s why when developing a theme it is essential to include a default template.

A static example of a default template for a block theme would be the following:

<header>
	<h1>Hello everyone</h1>
	<p>This is my first theme.</p>
  <hr>
</header>

<main>	
	<p>Welcome!</p>
</main>
HTML

In order to achieve a similar result for a classic theme, we would create an index.php with the following content:

<!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="//gmpg.org/xfn/11">
    <link rel="pingback" href="<?php bloginfo('pingback_url'); ?>">
		<?php wp_head(); ?>
	</head>

	<body <?php body_class();?>>
		
		<?php wp_body_open(); ?>
			
		<header>
			<h1>Hello everyone</h1>
			<p>This is my first theme.</p>
  		<hr>
		</header>
		
		<main>
			<p>Welcome!</p>
		</main>
		
		<?php wp_footer(); ?>
	
	</body>
</html>
PHP

In both cases, WordPress will render the same HTML no matter what kind of content we are trying to access, because it will always use the same template. But before we start differentiating our templates, let’s take a look at another major feature.

The Functions File

Every theme needs some kind of unique functionality. This is where functions.php comes into play. It is executed for every request before any content is rendered. However, it is not a mandatory file.

To start demonstrating functions.php‘s use, let’s make some changes to our main stylesheet. Let’s suppose we want the header to align at the center, and also be blue:

/*
Theme Name: Tutorial
*/

header {
	color: blue;
	text-align: center;
}
CSS

If we refresh our page, we will notice that nothing changed. That’s because WordPress does not include the main stylesheet by default. And that’s true for both classic and block themes. Now, let’s create a functions.php file in the root folder of our theme with the following content:

<?php

function tutorial_enqueue_scripts(){
	wp_enqueue_style('tutorial-style',get_stylesheet_uri());
}

add_action('wp_enqueue_scripts','tutorial_enqueue_scripts');
PHP

Now, if we refresh our page, we will see that our style is applied. Of course, this is not the only use of this file. It essentially contains the whole logic of the theme, outside of templates.

Template Parts

As we mentioned above, our example template renders the same content no matter what part of the site we are visiting. Let’s move forward a bit and diversify our templates. For example, let’s say we want to show the title and content of a post or page, and fall back to the Welcome! paragraph in all other cases. The <header> part will be visible across the site.

It is obvious that now we need two different templates with a common header. Let’s do that using template parts, for both classic and block themes.

For classic themes

As you might have noticed, in our classic version of the default template we included all the HTML/PHP needed to render our content. That means that we have common parts across templates, both above and below the <main> section. Consequently, we have to create two template parts in the root folder of our theme.

A 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="//gmpg.org/xfn/11">
    <link rel="pingback" href="<?php bloginfo('pingback_url'); ?>">
		<?php wp_head(); ?>
	</head>

	<body <?php body_class();?>>
		
		<?php wp_body_open(); ?>
			
		<header>
			<h1>Hello everyone</h1>
			<p>This is my first theme.</p>
  		<hr>
		</header>
		
		<main>
PHP

And a footer.php:

		</main>
		
		<?php wp_footer(); ?>
	
	</body>
</html>
PHP

Our index.php will now look like this:

<?php get_header(); ?>

<p>Welcome!</p>

<?php get_footer(); ?>
PHP

And finally we need to create a singular.php in the theme’s root folder:

<?php get_header(); ?>

<h1><?php the_title(); ?></h1>
<div><?php the_content(); ?></div>

<?php get_footer(); ?>
PHP

For block themes

Evidently, in a block theme we don’t have to include all the HTML needed, so we will only need a header template part and two templates. Template parts are stored in a special parts folder, so a parts/header.html file would look like this:

<h1>Hello everyone</h1>
<p>This is my first theme.</p>
<hr>
HTML

Our modified templates/index.html would be the following:

<!-- wp:template-part {"slug":"header","tagName":"header"} /-->

<main>	
	<p>Welcome!</p>
</main>
HTML

Lastly, we would have to create a templates/singular.php like so:

<!-- wp:template-part {"slug":"header","tagName":"header"} /-->

<main>	
	<!-- wp:post-title {"level":1} /-->
	<!-- wp:post-content /-->
</main>
HTML