Introduction to WordPress Themes
WordPress themes are the face of your website. While WordPress core provides the functionality and database structure, themes control how your content looks and is presented to visitors. This separation of content from presentation is a fundamental principle in WordPress architecture.
Think of WordPress itself as the skeletal and muscular system of a body, providing structure and function, while the theme is the skin, hair, and clothing – everything that determines appearance and presentation. This separation allows you to completely change how your site looks without affecting the underlying content or functionality.
What themes control:
- Visual design and aesthetics (colors, typography, etc.)
- Layout of content (columns, grids, positioning)
- User experience elements (navigation menus, sidebars)
- Responsive behavior across different devices
- Presentation of different content types (posts, pages, products)
- Visual aspects of comments, forms, and other interactive elements
- Integration of design with widgets and plugin output
What themes should NOT control:
- Core functionality that should persist across theme changes
- Custom post types vital to the site's operation
- Data storage or retrieval methods
- Backend administration interfaces
- Complex business logic
- Functionality that isn't directly related to presentation
- Features that would "lock in" users to your theme
A real-world analogy: Consider WordPress as a well-built house with solid foundations, plumbing, electricity, and rooms. The theme is like interior decoration, paint colors, furniture arrangement, and style choices. You can completely redecorate without moving walls or affecting the structure.
Theme Directory Structure
WordPress themes live in the /wp-content/themes/ directory of a WordPress installation. Each theme has its own folder, and you can switch between installed themes in the WordPress admin panel under Appearance → Themes.
Understanding the structure of a WordPress theme is like understanding the blueprint of a house. Each file has a specific purpose and place in the overall architecture.
wp-content/
└── themes/
├── twentytwentyfour/ # Default WordPress theme
└── my_custom_theme/ # Your custom theme
├── style.css # Required: Theme metadata and styles
├── index.php # Required: Main template file
├── functions.php # Theme functionality and features
├── header.php # Site header template
├── footer.php # Site footer template
├── sidebar.php # Sidebar template
├── single.php # Single post template
├── page.php # Static page template
├── archive.php # Archive pages template
├── search.php # Search results template
├── 404.php # 404 error page template
├── comments.php # Comments display and form
├── front-page.php # Site front page template
├── home.php # Blog posts index template
├── template-parts/ # Reusable template components
│ ├── content.php # Default content template part
│ ├── content-page.php # Page content part
│ └── content-single.php # Single post content part
├── assets/ # Theme assets
│ ├── css/ # Additional CSS files
│ ├── js/ # JavaScript files
│ └── images/ # Theme images
├── inc/ # Theme include files
│ ├── template-tags.php # Custom template functions
│ └── customizer.php # Theme customizer settings
├── languages/ # Translation files
├── screenshot.png # Theme thumbnail for admin
└── theme.json # Block editor settings (newer themes)
Required Theme Files
At minimum, a functional WordPress theme requires only two files:
style.css
This file serves two crucial purposes: it contains the theme's metadata (in a specially formatted comment header) and can include the theme's CSS styling.
/*
Theme Name: My Custom Theme
Theme URI: https://example.com/my-custom-theme
Author: Your Name
Author URI: https://yourwebsite.com
Description: A beautiful, responsive, and feature-rich WordPress theme.
Version: 1.0.0
Requires at least: 5.9
Tested up to: 6.4
Requires PHP: 7.4
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: my-custom-theme
Tags: blog, custom-background, custom-logo, custom-menu, featured-images
*/
/* Theme styles can go here */
body {
font-family: 'Helvetica Neue', Arial, sans-serif;
line-height: 1.6;
color: #333;
}
.site-header {
background-color: #f8f8f8;
padding: 1rem;
border-bottom: 1px solid #e8e8e8;
}
The comment header is what WordPress uses to identify and display your theme in the admin interface. Think of it like the product label and information for your theme.
index.php
This is the main template file and serves as the fallback template when no other template file matches the current request. It's like the default blueprint that gets used when no specialized blueprints are available.
<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo('charset'); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<?php wp_head(); ?>
</head>
<body <?php body_class(); ?>>
<?php wp_body_open(); ?>
<header class="site-header">
<div class="site-branding">
<h1><a href="<?php echo esc_url(home_url('/')); ?>"><?php bloginfo('name'); ?></a></h1>
<p class="site-description"><?php bloginfo('description'); ?></p>
</div>
<nav class="main-navigation">
<?php
wp_nav_menu(array(
'theme_location' => 'primary',
'menu_id' => 'primary-menu',
));
?>
</nav>
</header>
<main class="site-content">
<?php
if (have_posts()) :
while (have_posts()) :
the_post();
?>
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
<header class="entry-header">
<h2 class="entry-title">
<a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
</h2>
</header>
<div class="entry-content">
<?php the_content(); ?>
</div>
</article>
<?php
endwhile;
the_posts_navigation();
else :
echo '<p>No content found</p>';
endif;
?>
</main>
<footer class="site-footer">
<p>© <?php echo date('Y'); ?> <?php bloginfo('name'); ?></p>
</footer>
<?php wp_footer(); ?>
</body>
</html>
This basic index.php file handles the HTML structure, header, footer, and the WordPress Loop for displaying content. In a complete theme, many of these elements would be moved to separate files like header.php and footer.php.
Common Optional Theme Files
While only style.css and index.php are required, professional WordPress themes typically include several additional files to create a more modular and flexible structure:
functions.php
This file works like a mini-plugin specific to your theme. It's where you register features, enqueue styles and scripts, create custom functions, and extend WordPress functionality for your theme.
Think of functions.php as the "operations manual" for your theme - it tells WordPress what your theme can do and how it should behave.
<?php
/**
* Theme functions and definitions
*/
// Theme setup
function mytheme_setup() {
// Add theme support for various features
add_theme_support('automatic-feed-links');
add_theme_support('title-tag');
add_theme_support('post-thumbnails');
add_theme_support('html5', array(
'search-form',
'comment-form',
'comment-list',
'gallery',
'caption',
));
add_theme_support('customize-selective-refresh-widgets');
add_theme_support('responsive-embeds');
// Register navigation menus
register_nav_menus(array(
'primary' => esc_html__('Primary Menu', 'my-custom-theme'),
'footer' => esc_html__('Footer Menu', 'my-custom-theme'),
));
}
add_action('after_setup_theme', 'mytheme_setup');
// Register widget area (sidebar)
function mytheme_widgets_init() {
register_sidebar(array(
'name' => esc_html__('Sidebar', 'my-custom-theme'),
'id' => 'sidebar-1',
'description' => esc_html__('Add widgets here.', 'my-custom-theme'),
'before_widget' => '<section id="%1$s" class="widget %2$s">',
'after_widget' => '</section>',
'before_title' => '<h2 class="widget-title">',
'after_title' => '</h2>',
));
}
add_action('widgets_init', 'mytheme_widgets_init');
// Enqueue scripts and styles
function mytheme_scripts() {
// Enqueue main stylesheet
wp_enqueue_style('my-custom-theme-style', get_stylesheet_uri(), array(), '1.0.0');
// Add additional CSS
wp_enqueue_style('my-custom-theme-main', get_template_directory_uri() . '/assets/css/main.css', array(), '1.0.0');
// Add JavaScript
wp_enqueue_script('my-custom-theme-navigation', get_template_directory_uri() . '/assets/js/navigation.js', array(), '1.0.0', true);
// Add comment reply script if needed
if (is_singular() && comments_open() && get_option('thread_comments')) {
wp_enqueue_script('comment-reply');
}
}
add_action('wp_enqueue_scripts', 'mytheme_scripts');
/**
* Custom template tags for this theme.
*/
require get_template_directory() . '/inc/template-tags.php';
/**
* Customizer additions.
*/
require get_template_directory() . '/inc/customizer.php';
header.php
Contains the opening HTML, head section, and typically the site header and navigation menu. This file is included at the top of most template files using get_header().
The header.php file is like the standard introduction to your website - it establishes the context and navigation for visitors.
footer.php
Contains the footer section and closing HTML tags. This file is included at the bottom of most template files using get_footer().
Similar to how books have consistent endings with publishing information, footers typically contain copyright, additional navigation, and contact information.
sidebar.php
Contains the sidebar content, usually widgets or additional navigation. This file is included using get_sidebar().
Sidebars are like the complementary sections of a newspaper or magazine - they provide additional context, navigation, or related content.
single.php
Template for displaying single blog posts. When a visitor views an individual post, WordPress uses this template.
page.php
Template for displaying static pages. When a visitor views a page (not a post), WordPress uses this template.
The modular approach with multiple template files allows for cleaner code organization and makes it easier to maintain and update your theme. It also provides more specific control over how different types of content are displayed.
WordPress Template Hierarchy
The Template Hierarchy is one of WordPress's most powerful concepts. It's a system that determines which template file to use when a particular page is requested. This system allows for incredibly specific templates for different types of content while providing sensible fallbacks.
Think of the template hierarchy as a decision tree or like the triage process in an emergency room. WordPress examines the request, determines what kind of content is being requested, and then looks for increasingly specific templates before falling back to more general ones.
This flowchart represents a simplified version of how WordPress decides which template to use. The complete hierarchy is more extensive and handles many more specific cases.
Complete Template Hierarchy
The official WordPress template hierarchy diagram illustrates all possible template paths:
Source: WordPress Developer Resources
Practical Examples of Template Selection
Scenario 1: Viewing a Single Blog Post
When a visitor views a blog post with the URL https://example.com/2024/05/my-awesome-post/, WordPress looks for templates in this order:
single-post-my-awesome-post.php(Ultra-specific template for this exact post)single-post.php(Template for all posts)single.php(Template for all single content)singular.php(Template for any single content including pages)index.php(Fallback template)
Scenario 2: Viewing a Product Page (Custom Post Type)
For a product page with URL https://example.com/product/ergonomic-chair/, WordPress looks for:
single-product-ergonomic-chair.php(Template for this specific product)single-product.php(Template for all products)single.php(Template for all single content)singular.php(Template for any single content)index.php(Fallback template)
Scenario 3: Viewing an About Page
For a page with URL https://example.com/about/, WordPress looks for:
page-about.phporpage-2.php(if 2 is the page ID) (Template for this specific page)page.php(Template for all pages)singular.php(Template for any single content)index.php(Fallback template)
Scenario 4: Viewing the Front Page
For the site's front page, WordPress looks for:
front-page.php(Specific front page template)- If the front page is set to show the blog:
home.phpthenindex.php - If the front page is set to a static page:
page-{slug}.php,page-{id}.php,page.php,singular.php,index.php
Scenario 5: Viewing a Category Archive
For a category archive page like https://example.com/category/news/, WordPress looks for:
category-news.phporcategory-5.php(if 5 is the category ID) (Template for this specific category)category.php(Template for all categories)archive.php(Template for all archives)index.php(Fallback template)
Real-world Application of Template Hierarchy
Understanding the template hierarchy gives you precise control over your site's appearance. Here are some practical applications:
- E-commerce site: Create a special template for your "sale" category (
category-sale.php) with a distinctive design and promotional elements, while keeping other categories in the standard design. - Portfolio site: Create a unique layout for your "case study" post type (
single-case-study.php) with wider images and client testimonials, different from your regular blog posts. - Magazine site: Create different layouts for different authors (
author-editor.php,author-contributor.php) to distinguish senior staff content. - Corporate site: Create a special template for your "About" page (
page-about.php) with team photos and company history, different from standard pages.
The template hierarchy works like the difference between mass-produced and custom-tailored clothing. You can use general templates for most content but create special, custom-designed templates for specific content that needs unique presentation.
Template Parts and Modular Development
Modern WordPress themes follow a modular approach, breaking down templates into smaller, reusable components called "template parts." This promotes code reuse, improves maintainability, and reduces duplication.
Using get_template_part()
WordPress provides the get_template_part() function to include template parts in your theme. This function works similarly to PHP's include, but with added benefits:
- It looks for template parts in both child and parent themes
- It allows for naming variations and fallbacks
- It helps organize code into logical components
- It supports passing variables (in newer WordPress versions)
Basic Usage:
// Include template-parts/content.php
get_template_part('template-parts/content');
// Include template-parts/content-video.php (or content.php if not found)
get_template_part('template-parts/content', 'video');
// Include with parameters (WordPress 5.5+)
get_template_part('template-parts/content', 'post', array(
'featured' => true,
'show_author' => true,
'post_id' => 123
));
Think of template parts like reusable building blocks or LEGO pieces. Instead of building the entire structure from scratch each time, you create standardized components that can be assembled in different ways.
Common Template Part Usage
Here's how a typical WordPress theme might use template parts:
Real Example of Modular Theme Organization
Let's see how this works in practice with a real-world example:
single.php (Main template for single posts):
<?php
/**
* The template for displaying all single posts
*/
get_header(); // Include header.php
?>
<div class="container">
<main id="primary" class="site-main">
<?php
while (have_posts()) :
the_post();
// Include the appropriate template part based on post type
// If this is a 'post', it will look for content-post.php
// If that doesn't exist, it falls back to content.php
get_template_part('template-parts/content', get_post_type());
// Include post navigation (previous/next)
get_template_part('template-parts/navigation', 'post');
// If comments are open or we have at least one comment, load comments
if (comments_open() || get_comments_number()) :
comments_template(); // Includes comments.php
endif;
// Include author bio section for posts only
if ('post' === get_post_type()) :
get_template_part('template-parts/author', 'bio');
endif;
// Include related posts section
get_template_part('template-parts/related', 'posts');
endwhile;
?>
</main>
<?php get_sidebar(); // Include sidebar.php ?>
</div>
<?php get_footer(); // Include footer.php ?>
template-parts/content-post.php (Template part for post content):
<?php
/**
* Template part for displaying posts
*/
// Access variables passed from get_template_part() (WP 5.5+)
$args = wp_parse_args($args, array(
'show_author' => true,
'featured' => false,
));
?>
<article id="post-<?php the_ID(); ?>" <?php post_class($args['featured'] ? 'featured-post' : ''); ?>>
<header class="entry-header">
<?php the_title('<h1 class="entry-title">', '</h1>'); ?>
<div class="entry-meta">
<?php
// Show post date
mysite_posted_on();
// Show author if enabled
if ($args['show_author']) {
mysite_posted_by();
}
// Show categories
mysite_categories();
?>
</div>
</header>
<?php if (has_post_thumbnail()) : ?>
<div class="post-thumbnail">
<?php the_post_thumbnail('large'); ?>
</div>
<?php endif; ?>
<div class="entry-content">
<?php
the_content();
wp_link_pages(array(
'before' => '<div class="page-links">' . esc_html__('Pages:', 'mysite'),
'after' => '</div>',
));
?>
</div>
<footer class="entry-footer">
<?php mysite_entry_footer(); ?>
</footer>
</article>
template-parts/author-bio.php (Template part for author information):
<?php
/**
* Template part for displaying author bio
*/
// Get author details
$author_id = get_the_author_meta('ID');
$author_posts_url = get_author_posts_url($author_id);
$author_description = get_the_author_meta('description');
// Only show author bio if a description exists
if (!$author_description) {
return;
}
?>
<div class="author-bio">
<h2 class="author-title">
<?php printf(esc_html__('About %s', 'mysite'), get_the_author()); ?>
</h2>
<div class="author-avatar">
<?php echo get_avatar($author_id, 100); ?>
</div>
<div class="author-description">
<p><?php echo $author_description; ?></p>
<a class="author-link" href="<?php echo esc_url($author_posts_url); ?>">
<?php printf(esc_html__('View all posts by %s', 'mysite'), get_the_author()); ?>
</a>
</div>
</div>
Benefits of Modular Theme Development
The modular approach offers numerous advantages:
- Maintainability: Smaller, focused files are easier to understand and update
- DRY (Don't Repeat Yourself): Code reuse reduces duplication and potential errors
- Collaboration: Multiple developers can work on different components simultaneously
- Customization: Child themes can selectively override specific template parts
- Organization: Clear structure makes it easier to find and modify specific components
- Performance: Only the necessary template parts are loaded for each page
This modularity is similar to how modern manufacturing works. Rather than building each product from scratch, companies create standardized components that can be assembled in different configurations. This improves quality, reduces costs, and allows for customization within a common framework.
Template Tags and Functions
WordPress provides a comprehensive set of template tags and functions that serve as the building blocks for theme templates. These functions output dynamic content, perform common operations, and help connect your templates to the WordPress database.
Think of template tags as the connectors or interfaces between your static HTML templates and the dynamic content stored in the WordPress database. They're like electrical outlets in a house, providing standardized ways to access the power (content) behind the walls.
Essential Template Tags by Category
The Difference Between "get_" and "the_" Functions
Many WordPress functions come in two versions:
- "the_" functions (e.g.,
the_title()) - Display information directly to the page - "get_" functions (e.g.,
get_the_title()) - Return information for you to use in PHP
Example of get_ vs the_ functions:
// the_title() directly outputs the title
the_title(); // Output: "My Amazing Post Title"
// get_the_title() returns the title without outputting it
$title = get_the_title();
echo 'Post title: ' . $title; // Output: "Post title: My Amazing Post Title"
// You can manipulate the returned value before displaying
$title = get_the_title();
$uppercase_title = strtoupper($title);
echo $uppercase_title; // Output: "MY AMAZING POST TITLE"
// You can use the returned value in conditions
if (strpos(get_the_title(), 'Featured') !== false) {
echo 'This is a featured post!';
}
Practical Examples of Template Tags in Use
Let's see how template tags are used in real-world scenarios:
Example 1: Displaying a Post with Featured Image
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
<header class="entry-header">
<?php if (is_singular()) : ?>
<h1 class="entry-title"><?php the_title(); ?></h1>
<?php else : ?>
<h2 class="entry-title">
<a href="<?php the_permalink(); ?>" rel="bookmark"><?php the_title(); ?></a>
</h2>
<?php endif; ?>
<div class="entry-meta">
<span class="posted-on">
Posted on <a href="<?php the_permalink(); ?>"><?php the_time('F j, Y'); ?></a>
</span>
<span class="byline">
by <a href="<?php echo esc_url(get_author_posts_url(get_the_author_meta('ID'))); ?>">
<?php the_author(); ?>
</a>
</span>
</div>
</header>
<?php if (has_post_thumbnail()) : ?>
<div class="post-thumbnail">
<a href="<?php the_permalink(); ?>">
<?php the_post_thumbnail('large'); ?>
</a>
</div>
<?php endif; ?>
<div class="entry-content">
<?php
if (is_singular()) :
the_content();
else :
the_excerpt();
?>
<a href="<?php the_permalink(); ?>" class="read-more">
Continue reading <span class="screen-reader-text"><?php the_title(); ?></span>
</a>
<?php endif; ?>
</div>
<footer class="entry-footer">
<?php
// Display categories
$categories_list = get_the_category_list(', ');
if ($categories_list) {
echo '<span class="cat-links">Posted in ' . $categories_list . '</span>';
}
// Display tags
$tags_list = get_the_tag_list('', ', ');
if ($tags_list) {
echo '<span class="tags-links">Tagged ' . $tags_list . '</span>';
}
?>
</footer>
</article>
Example 2: Conditional Content Based on Post Type
<?php
// Different layouts for different post types
if (is_singular('product')) :
// Product template
?>
<div class="product-container">
<div class="product-gallery">
<?php the_post_thumbnail('product-large'); ?>
<?php
// Display product gallery
if (function_exists('product_gallery')) {
product_gallery();
}
?>
</div>
<div class="product-details">
<h1 class="product-title"><?php the_title(); ?></h1>
<div class="product-price"><?php product_price(); ?></div>
<div class="product-description"><?php the_content(); ?></div>
<div class="product-meta"><?php product_meta(); ?></div>
</div>
</div>
<?php
elseif (is_singular('portfolio')) :
// Portfolio template
?>
<div class="portfolio-container">
<div class="portfolio-showcase">
<?php the_post_thumbnail('portfolio-full'); ?>
</div>
<div class="portfolio-content">
<h1 class="portfolio-title"><?php the_title(); ?></h1>
<div class="client-info">
<strong>Client:</strong> <?php echo get_post_meta(get_the_ID(), 'client_name', true); ?>
</div>
<div class="project-date">
<strong>Completed:</strong> <?php echo get_post_meta(get_the_ID(), 'project_date', true); ?>
</div>
<div class="portfolio-description"><?php the_content(); ?></div>
</div>
</div>
<?php
else :
// Default post template
?>
<article class="standard-post">
<h1 class="entry-title"><?php the_title(); ?></h1>
<div class="entry-meta"><?php posted_on(); ?></div>
<?php if (has_post_thumbnail()) : ?>
<div class="featured-image"><?php the_post_thumbnail(); ?></div>
<?php endif; ?>
<div class="entry-content"><?php the_content(); ?></div>
</article>
<?php endif; ?>
These template tags form the vocabulary of WordPress theme development. Learning to use them effectively is essential for creating dynamic, content-driven websites with WordPress.
Child Themes
Child themes are one of WordPress's most powerful features for theme customization. They allow you to modify an existing theme without altering its original files, ensuring your changes won't be lost during theme updates.
Think of a child theme as inheritance in object-oriented programming, or like renovating a house without changing its foundation. You keep all the functionality and structure of the parent theme while adding your own customizations on top.
When to Use Child Themes
Perfect for:
- Customizing an existing theme that receives regular updates
- Making design changes to a theme without touching its code
- Adding new functionality to a theme
- Creating variations of a theme for different clients/projects
- Learning theme development by extending an established theme
Not ideal for:
- Building a completely different theme from scratch
- Making extensive changes to most aspects of the parent theme
- Working with poorly coded parent themes
- When the parent theme is unlikely to be updated
Creating a Child Theme
At minimum, a child theme requires two files:
- style.css - Defines the theme information and can override the parent theme's styles
- functions.php - Adds functionality and properly enqueues parent/child stylesheets
Step 1: Create a child theme folder
Create a new folder in /wp-content/themes/ with a name like twentytwentyfour-child
Step 2: Create style.css with theme information
/*
Theme Name: Twenty Twenty-Four Child
Theme URI: https://example.com/twenty-twenty-four-child/
Description: A child theme of Twenty Twenty-Four
Author: Your Name
Author URI: https://yourwebsite.com
Template: twentytwentyfour
Version: 1.0
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: twentytwentyfour-child
*/
/* Add custom styles here to override parent theme styles */
body {
font-family: 'Roboto', sans-serif;
}
.site-header {
background-color: #3498db;
color: white;
}
.entry-title {
color: #2c3e50;
}
Note the Template line - this tells WordPress which theme is the parent. It must match the parent theme's folder name exactly.
Step 3: Create functions.php to properly enqueue styles
<?php
/**
* Twenty Twenty-Four Child Theme functions
*/
// Enqueue parent and child theme stylesheets correctly
function child_theme_styles() {
// Get parent theme version for cache busting
$parent_style = 'twentytwentyfour-style';
$parent_theme = wp_get_theme('twentytwentyfour');
// Enqueue parent theme's stylesheet
wp_enqueue_style(
$parent_style,
get_template_directory_uri() . '/style.css',
array(),
$parent_theme->get('Version')
);
// Enqueue child theme's stylesheet
wp_enqueue_style(
'child-style',
get_stylesheet_uri(),
array($parent_style),
wp_get_theme()->get('Version')
);
}
add_action('wp_enqueue_scripts', 'child_theme_styles');
// Add any additional functions for your child theme below
function child_theme_setup() {
// Example: Add a new image size
add_image_size('featured-large', 1200, 600, true);
// Example: Register an additional menu location
register_nav_menus(array(
'tertiary' => esc_html__('Tertiary Menu', 'twentytwentyfour-child'),
));
}
add_action('after_setup_theme', 'child_theme_setup');
Overriding Parent Theme Files
To customize specific template files from the parent theme, simply create files with the same name in your child theme. WordPress will use the child theme version instead.
Example: Overriding the header.php file
1. Copy header.php from the parent theme to your child theme folder
2. Modify the file as needed
<!-- This is the CHILD theme's header.php -->
<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo('charset'); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<?php wp_head(); ?>
<!-- Custom tracking code for the child theme -->
<script>
// Custom analytics code here
</script>
</head>
<body <?php body_class(); ?>>
<?php wp_body_open(); ?>
<div id="page" class="site">
<header id="masthead" class="site-header custom-header">
<div class="site-branding">
<?php if (has_custom_logo()) : ?>
<div class="site-logo"><?php the_custom_logo(); ?></div>
<?php else : ?>
<h1 class="site-title">
<a href="<?php echo esc_url(home_url('/')); ?>" rel="home">
<?php bloginfo('name'); ?>
</a>
</h1>
<?php endif; ?>
<?php
$description = get_bloginfo('description', 'display');
if ($description) : ?>
<p class="site-description"><?php echo $description; ?></p>
<?php endif; ?>
</div>
<nav id="site-navigation" class="main-navigation">
<button class="menu-toggle" aria-controls="primary-menu" aria-expanded="false">
<span class="menu-toggle-text"><?php esc_html_e('Menu', 'twentytwentyfour-child'); ?></span>
</button>
<?php
wp_nav_menu(array(
'theme_location' => 'primary',
'menu_id' => 'primary-menu',
'container_class' => 'primary-menu-container',
));
?>
<?php get_search_form(); ?>
</nav>
<!-- Custom announcement banner added in child theme -->
<div class="announcement-banner">
<?php echo get_theme_mod('announcement_text', 'Welcome to our website!'); ?>
</div>
</header>
Overriding Template Parts
You can also override specific template parts by creating matching files in your child theme's structure.
Example: Overriding content-page.php
1. Create the template-parts directory in your child theme
2. Create content-page.php with your custom implementation:
<?php
/**
* Template part for displaying page content in page.php
*
* Overridden in child theme to add custom page features
*/
?>
<article id="post-<?php the_ID(); ?>" <?php post_class('enhanced-page'); ?>>
<?php if (!is_front_page()) : ?>
<header class="entry-header custom-page-header">
<?php the_title('<h1 class="entry-title">', '</h1>'); ?>
<!-- Custom breadcrumbs added in child theme -->
<div class="breadcrumbs">
<?php
if (function_exists('yoast_breadcrumb')) {
yoast_breadcrumb('<p id="breadcrumbs">', '</p>');
}
?>
</div>
</header>
<?php endif; ?>
<?php if (has_post_thumbnail()) : ?>
<div class="page-featured-image">
<?php the_post_thumbnail('full', array('class' => 'custom-featured-image')); ?>
<?php if (get_post_meta(get_the_ID(), 'image_credit', true)) : ?>
<div class="image-credit">
Photo: <?php echo get_post_meta(get_the_ID(), 'image_credit', true); ?>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
<div class="entry-content enhanced-content">
<?php
the_content();
wp_link_pages(array(
'before' => '<div class="page-links">' . esc_html__('Pages:', 'twentytwentyfour-child'),
'after' => '</div>',
));
?>
<!-- Custom page attributes added in child theme -->
<?php if (get_post_meta(get_the_ID(), 'page_last_updated', true)) : ?>
<div class="page-meta">
<p class="last-updated">
Last updated: <?php echo get_post_meta(get_the_ID(), 'page_last_updated', true); ?>
</p>
</div>
<?php endif; ?>
</div>
</article>
Real-world Child Theme Examples
Here are some practical ways child themes are used in real projects:
- Branding customizations: Change colors, fonts, and logo on a standard theme to match client branding
- Layout modifications: Adjust the grid, column widths, or content positioning for specific project needs
- Feature additions: Add specialized features like social sharing, author bios, or related content
- Performance optimizations: Streamline a parent theme by removing unused features or optimizing code
- Multilingual adaptations: Modify templates to better support translation plugins or RTL languages
Child themes are like having the best of both worlds - the solid foundation and ongoing updates of the parent theme, combined with the precise customizations you need for your specific project.
Block Themes and Full Site Editing
WordPress has been evolving toward a new paradigm called Full Site Editing (FSE) with the introduction of block themes. This represents a significant shift in how themes are built and how users can customize their sites.
Traditional Themes vs. Block Themes
| Feature | Traditional Themes | Block Themes |
|---|---|---|
| Template Structure | PHP templates (header.php, single.php, etc.) | HTML files with block markup |
| Primary Language | PHP with HTML, CSS, JavaScript | HTML with block comments, CSS (in theme.json), JavaScript |
| Customization Method | Code editing or Customizer API | Visual, drag-and-drop in Site Editor |
| Style Control | CSS files with occasional inline styles | theme.json for global styles with visual controls |
| Template Parts | Included via PHP functions | Defined as reusable block patterns |
| User Editing Capability | Limited to what's exposed in Customizer | Can edit entire site structure |
| Developer Learning Curve | Steeper (requires PHP knowledge) | Potentially gentler (HTML-based) |
| User Learning Curve | Simpler for basic content editing | More complex, but more powerful |
The shift to block themes is similar to how website building evolved from hand-coding HTML to using visual page builders - it makes powerful customization capabilities accessible to more users without requiring extensive coding knowledge.
Block Theme Structure
Block themes have a different directory structure compared to traditional themes:
block-theme/
├── style.css # Theme metadata (minimal CSS)
├── functions.php # Theme functionality (minimized)
├── theme.json # Global styles and settings
├── templates/ # Block-based templates (HTML)
│ ├── index.html # Main fallback template
│ ├── singular.html # Template for single posts/pages
│ ├── archive.html # Template for archives
│ ├── 404.html # Template for 404 pages
│ └── page-about.html # Template for specific page
├── parts/ # Reusable template parts
│ ├── header.html # Header template part
│ ├── footer.html # Footer template part
│ └── sidebar.html # Sidebar template part
├── patterns/ # Block patterns
│ ├── hero-section.php # Hero section pattern
│ └── testimonial-grid.php # Testimonial grid pattern
├── assets/ # Theme assets
│ ├── css/ # Additional CSS
│ ├── js/ # JavaScript files
│ └── images/ # Theme images
└── screenshot.png # Theme thumbnail
Block Template Format
Block theme templates are HTML files with special block markup in comments. Here's an example of a basic index.html template:
<!-- wp:template-part {"slug":"header","tagName":"header"} /-->
<!-- wp:group {"tagName":"main","layout":{"type":"constrained"}} -->
<main class="wp-block-group">
<!-- wp:query {"queryId":1,"query":{"perPage":10,"pages":0,"offset":0,"postType":"post","order":"desc","orderBy":"date","author":"","search":"","exclude":[],"sticky":"","inherit":true}} -->
<div class="wp-block-query">
<!-- wp:post-template -->
<!-- wp:group {"layout":{"type":"constraine
<main class="wp-block-group">
<!-- wp:query {"queryId":1,"query":{"perPage":10,"pages":0,"offset":0,"postType":"post","order":"desc","orderBy":"date","author":"","search":"","exclude":[],"sticky":"","inherit":true}} -->
<div class="wp-block-query">
<!-- wp:post-template -->
<!-- wp:group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group">
<!-- wp:post-title {"isLink":true} /-->
<!-- wp:post-featured-image {"isLink":true} /-->
<!-- wp:post-excerpt /-->
<!-- wp:post-date /-->
</div>
<!-- /wp:group -->
<!-- /wp:post-template -->
<!-- wp:query-pagination -->
<!-- wp:query-pagination-previous /-->
<!-- wp:query-pagination-numbers /-->
<!-- wp:query-pagination-next /-->
<!-- /wp:query-pagination -->
<!-- wp:query-no-results -->
<!-- wp:paragraph -->
<p>No posts found.</p>
<!-- /wp:paragraph -->
<!-- /wp:query-no-results -->
</div>
<!-- /wp:query -->
</main>
<!-- /wp:group -->
<!-- wp:template-part {"slug":"footer","tagName":"footer"} /-->
This syntax might look strange at first, but it's essentially HTML with special comment blocks that define WordPress blocks. The comments follow a specific format that WordPress uses to identify and render blocks.
The theme.json File
The theme.json file is central to block themes. It defines global settings and styles that apply to both the editor and front end, providing a single source of truth for your theme's appearance.
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 2,
"settings": {
"color": {
"palette": [
{
"slug": "primary",
"color": "#0d6efd",
"name": "Primary"
},
{
"slug": "secondary",
"color": "#6c757d",
"name": "Secondary"
},
{
"slug": "light",
"color": "#f8f9fa",
"name": "Light"
},
{
"slug": "dark",
"color": "#212529",
"name": "Dark"
}
],
"gradients": [
{
"slug": "primary-to-secondary",
"gradient": "linear-gradient(135deg, #0d6efd 0%, #6c757d 100%)",
"name": "Primary to Secondary"
}
],
"duotone": [
{
"slug": "dark-grayscale",
"colors": ["#212529", "#f8f9fa"],
"name": "Dark grayscale"
}
]
},
"typography": {
"fontFamilies": [
{
"fontFamily": "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif",
"slug": "system-font",
"name": "System Font"
},
{
"fontFamily": "'Helvetica Neue', Helvetica, Arial, sans-serif",
"slug": "helvetica-arial",
"name": "Helvetica or Arial"
}
],
"fontSizes": [
{
"slug": "small",
"size": "0.875rem",
"name": "Small"
},
{
"slug": "medium",
"size": "1rem",
"name": "Medium"
},
{
"slug": "large",
"size": "1.5rem",
"name": "Large"
},
{
"slug": "x-large",
"size": "2rem",
"name": "Extra Large"
}
]
},
"spacing": {
"units": ["px", "em", "rem", "vh", "vw", "%"],
"spacingSizes": [
{
"slug": "small",
"size": "0.5rem",
"name": "Small"
},
{
"slug": "medium",
"size": "1rem",
"name": "Medium"
},
{
"slug": "large",
"size": "2rem",
"name": "Large"
}
]
},
"layout": {
"contentSize": "800px",
"wideSize": "1200px"
}
},
"styles": {
"color": {
"background": "var(--wp--preset--color--light)",
"text": "var(--wp--preset--color--dark)"
},
"typography": {
"fontSize": "var(--wp--preset--font-size--medium)",
"fontFamily": "var(--wp--preset--font-family--system-font)",
"lineHeight": "1.5"
},
"elements": {
"link": {
"color": {
"text": "var(--wp--preset--color--primary)"
},
":hover": {
"color": {
"text": "var(--wp--preset--color--secondary)"
}
}
},
"h1": {
"typography": {
"fontSize": "var(--wp--preset--font-size--x-large)",
"fontWeight": "700",
"lineHeight": "1.2"
},
"spacing": {
"margin": {
"top": "var(--wp--preset--spacing--large)",
"bottom": "var(--wp--preset--spacing--medium)"
}
}
}
},
"blocks": {
"core/paragraph": {
"spacing": {
"margin": {
"bottom": "var(--wp--preset--spacing--medium)"
}
}
},
"core/button": {
"color": {
"background": "var(--wp--preset--color--primary)",
"text": "white"
},
"border": {
"radius": "4px"
},
"typography": {
"fontSize": "var(--wp--preset--font-size--small)"
},
":hover": {
"color": {
"background": "var(--wp--preset--color--secondary)"
}
}
}
}
}
}
The theme.json file serves as a centralized configuration system, defining everything from color palettes and typography to spacing and layout options. It's similar to CSS design tokens or a design system specification.
Key Benefits of Block Themes
- Visual editing: Users can modify the entire site structure through a visual interface
- Consistency: The editor and front end share the same styles and appearance
- Global styles: Centralized control of design elements through theme.json
- Simplified development: Fewer PHP files to maintain
- Enhanced customization: More powerful options for users without coding
- Pattern libraries: Reusable block patterns for consistent design elements
Creating a Simple Block Theme
Let's walk through creating a minimal block theme:
1. Create the basic folder structure
my-block-theme/
├── style.css
├── theme.json
├── templates/
│ └── index.html
└── parts/
├── header.html
└── footer.html
2. Create style.css with theme information
/*
Theme Name: My Block Theme
Theme URI: https://example.com/my-block-theme
Author: Your Name
Author URI: https://yourwebsite.com
Description: A simple block theme for WordPress
Requires at least: 6.0
Tested up to: 6.4
Requires PHP: 7.4
Version: 1.0
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: my-block-theme
Tags: block-theme, full-site-editing, editor-style
*/
3. Create a minimal theme.json
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 2,
"settings": {
"color": {
"palette": [
{
"slug": "primary",
"color": "#007bff",
"name": "Primary"
},
{
"slug": "secondary",
"color": "#6c757d",
"name": "Secondary"
},
{
"slug": "background",
"color": "#ffffff",
"name": "Background"
},
{
"slug": "text",
"color": "#333333",
"name": "Text"
}
]
},
"typography": {
"fontFamilies": [
{
"fontFamily": "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
"slug": "system-font",
"name": "System Font"
}
],
"fontSizes": [
{
"slug": "small",
"size": "0.875rem",
"name": "Small"
},
{
"slug": "medium",
"size": "1rem",
"name": "Medium"
},
{
"slug": "large",
"size": "1.5rem",
"name": "Large"
}
]
},
"layout": {
"contentSize": "800px",
"wideSize": "1100px"
}
}
}
4. Create parts/header.html
<!-- wp:group {"style":{"spacing":{"padding":{"top":"var:preset|spacing|30","bottom":"var:preset|spacing|30"}},"border":{"bottom":{"color":"var:preset|color|secondary","width":"1px"}}},"backgroundColor":"background","layout":{"type":"constrained"}} -->
<div class="wp-block-group has-background-background-color has-background" style="border-bottom-color:var(--wp--preset--color--secondary);border-bottom-width:1px;padding-top:var(--wp--preset--spacing--30);padding-bottom:var(--wp--preset--spacing--30)">
<!-- wp:group {"layout":{"type":"flex","flexWrap":"nowrap","justifyContent":"space-between"}} -->
<div class="wp-block-group">
<!-- wp:site-title {"level":1} /-->
<!-- wp:navigation {"layout":{"type":"flex","orientation":"horizontal"}} /-->
</div>
<!-- /wp:group -->
</div>
<!-- /wp:group -->
5. Create parts/footer.html
<!-- wp:group {"style":{"spacing":{"padding":{"top":"var:preset|spacing|40","bottom":"var:preset|spacing|40"}},"border":{"top":{"color":"var:preset|color|secondary","width":"1px"}}},"backgroundColor":"background","layout":{"type":"constrained"}} -->
<div class="wp-block-group has-background-background-color has-background" style="border-top-color:var(--wp--preset--color--secondary);border-top-width:1px;padding-top:var(--wp--preset--spacing--40);padding-bottom:var(--wp--preset--spacing--40)">
<!-- wp:paragraph {"align":"center"} -->
<p class="has-text-align-center">© <!-- wp:site-title {"level":0} /-->, <!-- wp:pattern {"slug":"core/post-date"} /--></p>
<!-- /wp:paragraph -->
</div>
<!-- /wp:group -->
6. Create templates/index.html
<!-- wp:template-part {"slug":"header"} /-->
<!-- wp:group {"tagName":"main","style":{"spacing":{"margin":{"top":"var:preset|spacing|40","bottom":"var:preset|spacing|40"}}},"layout":{"type":"constrained"}} -->
<main class="wp-block-group" style="margin-top:var(--wp--preset--spacing--40);margin-bottom:var(--wp--preset--spacing--40)">
<!-- wp:query {"queryId":1,"query":{"perPage":10,"pages":0,"offset":0,"postType":"post","order":"desc","orderBy":"date","author":"","search":"","exclude":[],"sticky":"","inherit":true}} -->
<div class="wp-block-query">
<!-- wp:post-template -->
<!-- wp:post-title {"isLink":true} /-->
<!-- wp:post-featured-image {"isLink":true} /-->
<!-- wp:post-excerpt /-->
<!-- wp:separator {"backgroundColor":"secondary","className":"is-style-wide"} -->
<hr class="wp-block-separator has-text-color has-secondary-color has-alpha-channel-opacity has-secondary-background-color has-background is-style-wide"/>
<!-- /wp:separator -->
<!-- /wp:post-template -->
<!-- wp:query-pagination -->
<!-- wp:query-pagination-previous /-->
<!-- wp:query-pagination-numbers /-->
<!-- wp:query-pagination-next /-->
<!-- /wp:query-pagination -->
</div>
<!-- /wp:query -->
</main>
<!-- /wp:group -->
<!-- wp:template-part {"slug":"footer"} /-->
This basic structure provides a foundation for a block theme. From here, you would add more templates (single.html, archive.html, etc.) and expand your theme.json with additional style settings.
The Future of WordPress Themes
Block themes represent the future direction of WordPress development. As Full Site Editing continues to mature, we can expect:
- More sophisticated visual editing capabilities
- Enhanced theme.json options for styling
- Improved block pattern libraries
- Better integration between themes and plugins
- More drag-and-drop customization options
- A shift away from PHP-based theme development toward HTML/CSS focus
Understanding both traditional and block-based theme structures will be important during this transition period. Many themes will continue to use the traditional approach, while new themes increasingly adopt the block paradigm.
Practical Exercises
Exercise 1: Explore the Template Hierarchy
Install a popular WordPress theme like Twenty Twenty-Four and examine its file structure:
- Download a copy of the theme files to your local machine
- Identify all template files and map them to the template hierarchy
- Create a diagram showing which template is used for different content types
- Locate the template parts and understand how they're included in templates
Exercise 2: Create a Child Theme
Create a child theme for Twenty Twenty-Four (or another WordPress theme):
- Set up the basic child theme files (style.css and functions.php)
- Override the header.php file with a custom version
- Create a custom template part for post content
- Add custom CSS styles to modify the appearance
- Enqueue a custom JavaScript file
Exercise 3: Template Tag Practice
Create a custom page template that uses various template tags:
- Create a page-template-custom.php file in your theme
- Implement a two-column layout with the main content and a sidebar
- Use at least 10 different WordPress template tags
- Add conditional content that only appears for certain pages
- Include custom meta data using get_post_meta()
Exercise 4: Block Theme Exploration
If you're interested in the future of WordPress themes:
- Install a block theme like Twenty Twenty-Four
- Examine its file structure and compare it to traditional themes
- Look at the theme.json file and understand its settings
- Use the Site Editor to customize the theme
- Export a modified template and examine its block syntax
Resources and Further Reading
Summary
- WordPress themes control the visual presentation of your website while separating content from design
- At minimum, a theme requires style.css (with theme metadata) and index.php
- The Template Hierarchy determines which template file WordPress uses for different content types
- Template parts allow for modular theme development with reusable components
- Template tags are PHP functions that output dynamic content from the WordPress database
- Child themes let you customize an existing theme without modifying its files
- Block themes represent the future of WordPress with visual editing and theme.json configuration
Understanding WordPress theme structure and the template hierarchy is foundational for theme development. Whether you're building custom themes from scratch, modifying existing themes, or working with the new block theme paradigm, these concepts will help you create effective, maintainable WordPress sites.