Creating a Basic Theme Structure
Learning Objectives
- Understand the minimum required files for a WordPress theme
- Create proper theme metadata in style.css
- Build a functional index.php template
- Design and implement a theme screenshot
- Organize theme files following WordPress best practices
Introduction
Creating a WordPress theme is like building a house - you need a solid foundation before adding walls, windows, and decorations. Today, we'll lay that foundation by creating the essential files every WordPress theme must have.
The Two Required Files
WordPress requires only TWO files for a theme to be recognized. Think of these as the birth certificate and the body of your theme:
flowchart TD
A[WordPress Theme] --> B[style.css]
A --> C[index.php]
B --> B1[Theme Information]
B --> B2[Theme Name]
B --> B3[Author Details]
B --> B4[Version]
C --> C1[Template Logic]
C --> C2[HTML Output]
C --> C3[The Loop]
style B fill:#fef3c7,stroke:#f59e0b,stroke-width:2px
style C fill:#dbeafe,stroke:#3b82f6,stroke-width:2px
style.css REQUIRED
This file serves two critical purposes:
- Theme Declaration: Contains metadata that tells WordPress "I'm a theme!"
- Styling: Contains all your CSS rules (though you can also use separate CSS files)
index.php REQUIRED
The ultimate fallback template - if WordPress can't find a more specific template, it uses index.php. Think of it as the "catch-all" template.
Creating style.css with Theme Metadata
The style.css file must start with a special comment block that contains your theme's information. This is like your theme's passport - it identifies who made it, what it's called, and other vital information:
/*
Theme Name: My Awesome Theme
Theme URI: https://example.com/my-awesome-theme
Author: Your Name
Author URI: https://yourwebsite.com
Description: A beautiful and responsive WordPress theme that showcases modern design principles and best practices. Perfect for blogs, portfolios, and business websites.
Version: 1.0.0
License: GPL v2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: my-awesome-theme
Tags: blog, portfolio, custom-colors, custom-menu, featured-images, responsive-layout, translation-ready
*/
/* Theme styles start here */
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
line-height: 1.6;
color: #333;
margin: 0;
padding: 0;
}
.site-header {
background: #2c3e50;
color: white;
padding: 2rem 0;
}
.site-title {
margin: 0;
font-size: 2rem;
}
.site-main {
max-width: 1200px;
margin: 2rem auto;
padding: 0 1rem;
}
article {
margin-bottom: 3rem;
}
.entry-title {
color: #2c3e50;
margin-bottom: 1rem;
}
.entry-meta {
color: #7f8c8d;
font-size: 0.9rem;
margin-bottom: 1rem;
}
| Field | Required? | Purpose | Example |
|---|---|---|---|
| Theme Name | Yes | Your theme's display name | My Awesome Theme |
| Author | Recommended | Theme creator's name | John Doe |
| Description | Recommended | Brief theme description | A modern blog theme |
| Version | Recommended | Theme version number | 1.0.0 |
| License | Required for public themes | Theme license type | GPL v2 or later |
| Text Domain | For translation | Unique identifier for translations | my-awesome-theme |
| Tags | Optional | Theme features/categories | blog, responsive, custom-menu |
Building index.php
The index.php file is your theme's workhorse. It needs to handle displaying posts, pages, and any other content type. Here's a complete, functional index.php:
<?php
/**
* The main template file
*
* This is the most generic template file in a WordPress theme
* and one of the two required files for a theme (the other being style.css).
*
* @package My_Awesome_Theme
*/
get_header(); ?>
<main id="primary" class="site-main">
<div class="container">
<?php if ( have_posts() ) : ?>
<?php if ( is_home() && ! is_front_page() ) : ?>
<header class="page-header">
<h1 class="page-title"><?php single_post_title(); ?></h1>
</header>
<?php endif; ?>
<?php
// Start the Loop
while ( have_posts() ) :
the_post();
?>
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
<header class="entry-header">
<?php
if ( is_singular() ) :
the_title( '<h1 class="entry-title">', '</h1>' );
else :
the_title( '<h2 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h2>' );
endif;
?>
<div class="entry-meta">
<span class="posted-on">
Posted on <time datetime="<?php echo esc_attr( get_the_date( 'c' ) ); ?>">
<?php echo esc_html( get_the_date() ); ?>
</time>
</span>
<span class="posted-by">
by <?php the_author_posts_link(); ?>
</span>
</div>
</header>
<div class="entry-content">
<?php
if ( is_singular() ) :
the_content();
wp_link_pages( array(
'before' => '<div class="page-links">Pages:',
'after' => '</div>',
) );
else :
the_excerpt();
?>
<a href="<?php echo esc_url( get_permalink() ); ?>" class="read-more">
Continue reading →
</a>
<?php
endif;
?>
</div>
<footer class="entry-footer">
<?php
$categories = get_the_category_list( ', ' );
if ( $categories ) :
?>
<span class="cat-links">
Categories: <?php echo $categories; ?>
</span>
<?php
endif;
$tags = get_the_tag_list( '', ', ' );
if ( $tags ) :
?>
<span class="tags-links">
Tags: <?php echo $tags; ?>
</span>
<?php
endif;
?>
</footer>
</article>
<?php
endwhile;
// Pagination
the_posts_pagination( array(
'mid_size' => 2,
'prev_text' => '← Previous',
'next_text' => 'Next →',
) );
else :
?>
<article class="no-results not-found">
<header class="page-header">
<h1 class="page-title">Nothing Found</h1>
</header>
<div class="page-content">
<?php if ( is_home() && current_user_can( 'publish_posts' ) ) : ?>
<p>Ready to publish your first post?
<a href="<?php echo esc_url( admin_url( 'post-new.php' ) ); ?>">
Get started here
</a>.</p>
<?php elseif ( is_search() ) : ?>
<p>Sorry, but nothing matched your search terms.
Please try again with different keywords.</p>
<?php get_search_form(); ?>
<?php else : ?>
<p>It seems we can't find what you're looking for.
Perhaps searching can help.</p>
<?php get_search_form(); ?>
<?php endif; ?>
</div>
</article>
<?php endif; ?>
</div>
</main>
<?php
get_sidebar();
get_footer();
Understanding The Loop
The Loop is WordPress's way of cycling through posts. Think of it like a playlist:
Creating a Theme Screenshot
The screenshot.png file is your theme's visual identity in the WordPress admin. It's like the cover of a book - it should accurately represent your theme's design:
Screenshot Best Practices
- Dimensions: Exactly 1200px × 900px (WordPress will resize as needed)
- Format: PNG format for best quality
- Content: Show actual theme design, not logos or mockups
- Quality: Use high-quality images and clear typography
- Representation: Must accurately represent your theme's appearance
<!-- How to create a screenshot -->
1. Set up your theme with sample content
2. Navigate to your homepage
3. Use browser DevTools to set viewport to 1200px wide
4. Take a full-page screenshot
5. Crop to exactly 1200px × 900px
6. Save as screenshot.png in your theme root
<!-- Tools you can use: -->
- Chrome DevTools (Device Mode)
- Firefox Screenshot tool
- Photoshop/GIMP for editing
- Online tools like Canva or Figma
Complete Theme Directory Structure
While only two files are required, a professional theme needs proper organization. Here's the recommended structure:
graph TD
A[my-awesome-theme/] --> B[style.css]
A --> C[index.php]
A --> D[screenshot.png]
A --> E[functions.php]
A --> F[header.php]
A --> G[footer.php]
A --> H[sidebar.php]
A --> I[assets/]
A --> J[template-parts/]
A --> K[inc/]
I --> I1[css/]
I --> I2[js/]
I --> I3[images/]
I --> I4[fonts/]
J --> J1[content/]
J --> J2[header/]
J --> J3[footer/]
K --> K1[customizer.php]
K --> K2[template-functions.php]
K --> K3[template-tags.php]
style B fill:#fef3c7,stroke:#f59e0b,stroke-width:3px
style C fill:#dbeafe,stroke:#3b82f6,stroke-width:3px
style D fill:#fce7f3,stroke:#ec4899,stroke-width:2px
Testing Your Basic Theme
Once you've created your files, let's verify everything works:
<?php
// Quick test checklist:
// 1. Theme appears in WordPress admin?
// Navigate to Appearance > Themes
// 2. Screenshot displays correctly?
// Check the theme thumbnail
// 3. Theme activates without errors?
// Click "Activate" and check for PHP errors
// 4. Content displays on frontend?
// Visit your site and verify posts appear
// 5. Basic styling applied?
// Check that your CSS from style.css is loading
// Debug helper - add to index.php temporarily:
echo '<!-- Theme is loading correctly -->';
echo '<!-- Template: ' . basename(__FILE__) . ' -->';
echo '<!-- Theme directory: ' . get_template_directory_uri() . ' -->';
What You Should See
After activating your theme, you should see:
- ✅ Your site title at the top
- ✅ Posts displaying with titles and content
- ✅ Post metadata (date, author, categories)
- ✅ Pagination links if you have multiple posts
- ✅ Your custom styles from style.css applied
Practice Exercise
Let's build a minimal theme together:
<?php
// Minimal index.php starter
?>
<!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(); ?>>
<header>
<h1><?php bloginfo( 'name' ); ?></h1>
<p><?php bloginfo( 'description' ); ?></p>
</header>
<main>
<?php
if ( have_posts() ) :
while ( have_posts() ) : the_post();
?>
<article>
<h2><?php the_title(); ?></h2>
<?php the_content(); ?>
</article>
<?php
endwhile;
endif;
?>
</main>
<?php wp_footer(); ?>
</body>
</html>
Practice Assignment
Create your first complete WordPress theme with the following requirements:
- Create a theme folder with your name (e.g., "johns-first-theme")
- Include properly formatted style.css with all recommended metadata
- Build an index.php that displays posts, handles no content, and includes pagination
- Add at least 20 CSS rules for basic styling
- Create a professional screenshot.png (1200×900px)
- Include helpful code comments explaining your logic
- Test with at least 5 posts and 2 pages
- Bonus: Add a custom Google Font via functions.php