Themes
A WordPress theme is the presentation layer — templates, styles, scripts, theme.json. Modern WP supports both classic PHP themes and block (FSE) themes that edit visually in the Site Editor.
Classic vs block, anatomy, theme.json
EXAMPLE
<?php
/*
* Theme Name: My Theme
* Description: Custom theme for example.com
* Author: Ada Lovelace
* Version: 1.0.0
* Text Domain: my-theme
* Requires at least: 6.5
* Requires PHP: 8.1
*/
// === 1. functions.php — theme bootstrap ===
add_action('after_setup_theme', function () {
add_theme_support('post-thumbnails');
add_theme_support('title-tag');
add_theme_support('html5', ['search-form','comment-form','comment-list','gallery','caption','script','style']);
add_theme_support('responsive-embeds');
add_theme_support('wp-block-styles');
add_theme_support('align-wide');
add_theme_support('editor-styles');
add_editor_style('assets/css/editor.css');
register_nav_menus([
'primary' => __('Primary', 'my-theme'),
'footer' => __('Footer', 'my-theme'),
]);
});
add_action('wp_enqueue_scripts', function () {
$ver = wp_get_theme()->get('Version');
wp_enqueue_style(
'my-theme',
get_stylesheet_uri(),
[],
$ver,
);
wp_enqueue_script(
'my-theme-app',
get_stylesheet_directory_uri() . '/assets/js/app.js',
[],
$ver,
true, // in footer
);
});
// === 2. Template hierarchy (classic) ===
// WP picks templates in this order. First match wins.
// single-product.php → single.php → singular.php → index.php
// page-about.php → page-{slug}.php → page.php → singular.php → index.php
// category-news.php → category.php → archive.php → index.php
// 404.php
// front-page.php (homepage)
// home.php (blog index, if front page is static)
// === 3. The Loop — classic ===
// archive.php
get_header(); ?>
<main class="main">
<?php if (have_posts()) : ?>
<ul class="posts">
<?php while (have_posts()) : the_post(); ?>
<li>
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
<h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
<p class="meta"><?php the_time('F j, Y'); ?> by <?php the_author(); ?></p>
<div class="excerpt"><?php the_excerpt(); ?></div>
</article>
</li>
<?php endwhile; ?>
</ul>
<?php the_posts_navigation(); ?>
<?php else : ?>
<p>No posts found.</p>
<?php endif; ?>
</main>
<?php get_footer();
// === 4. Block themes (FSE) — templates in /templates and /parts ===
// theme structure:
// /templates/index.html
// /templates/page.html
// /templates/single.html
// /templates/404.html
// /parts/header.html
// /parts/footer.html
// theme.json
// functions.php
// style.css
// templates/single.html
// <!-- wp:template-part {"slug":"header","tagName":"header"} /-->
// <!-- wp:group {"tagName":"main"} -->
// <main>
// <!-- wp:post-title {"level":1} /-->
// <!-- wp:post-featured-image /-->
// <!-- wp:post-content /-->
// </main>
// <!-- /wp:group -->
// <!-- wp:template-part {"slug":"footer","tagName":"footer"} /-->
// === 5. theme.json — global styles + settings ===
// theme.json
{
"$schema": "https://schemas.wp.org/wp/6.5/theme.json",
"version": 2,
"settings": {
"color": {
"palette": [
{ "name": "Primary", "slug": "primary", "color": "#0ea5e9" },
{ "name": "Text", "slug": "text", "color": "#0b0f1a" },
{ "name": "BG", "slug": "bg", "color": "#ffffff" }
],
"customDuotone": false
},
"typography": {
"fontSizes": [
{ "name": "Small", "slug": "small", "size": "0.875rem" },
{ "name": "Base", "slug": "base", "size": "1rem" },
{ "name": "Large", "slug": "large", "size": "1.5rem" }
],
"fontFamilies": [
{ "name": "Inter", "slug": "inter", "fontFamily": "Inter, system-ui, sans-serif" }
]
},
"layout": {
"contentSize": "680px",
"wideSize": "1024px"
},
"spacing": {
"units": ["px","em","rem","vh","vw","%"],
"spacingScale": { "steps": 7 }
}
},
"styles": {
"color": {
"background": "var(--wp--preset--color--bg)",
"text": "var(--wp--preset--color--text)"
},
"typography": {
"fontFamily": "var(--wp--preset--font-family--inter)",
"fontSize": "var(--wp--preset--font-size--base)"
}
}
}
// === 6. Asset organisation ===
// /assets/css/main.css
// /assets/js/app.js
// /assets/img/...
// /assets/fonts/...
// === 7. Sanitise + escape EVERYTHING ===
<?php echo esc_html($title); ?> // text content
<?php echo esc_attr($css_class); ?> // attribute
<?php echo esc_url($url); ?> // href / src
<?php echo wp_kses_post($rich_html); ?> // limited HTML
// === 8. Child theme — extend a parent without modifying it ===
// Create:
// /wp-content/themes/twentytwentyfive-child/style.css (header below)
// /wp-content/themes/twentytwentyfive-child/functions.php
//
// style.css header:
/*
Theme Name: My Twenty Twenty-Five Child
Template: twentytwentyfive
Version: 1.0.0
*/
//
// functions.php — enqueue parent + child styles
add_action('wp_enqueue_scripts', function () {
wp_enqueue_style('parent-style', get_template_directory_uri() . '/style.css');
wp_enqueue_style(
'child-style',
get_stylesheet_directory_uri() . '/style.css',
['parent-style'],
wp_get_theme()->get('Version'),
);
});
// === 9. Internationalisation ===
add_action('after_setup_theme', function () {
load_theme_textdomain('my-theme', get_template_directory() . '/languages');
});
// Use: __('Hello', 'my-theme') / _e('Hello', 'my-theme')
// === 10. Best practices ===
// • Pin minimum PHP + WP versions in style.css header
// • Always escape on output
// • Use wp_enqueue_* (never echo <script> tags)
// • Block theme + theme.json — visually editable, future-proof
// • Page templates: register via Template Name comment
// • Custom block patterns: /patterns/*.php with block markup
// • CSS files served with versioned URL — bypass browser cache
Why it matters
Modern WordPress = block themes + theme.json. Classic PHP themes still work, but FSE lets clients edit visually without touching code — vastly less “please change this header” ticket flow.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
// Theme = look + templates. // wp-content/themes/your-theme/style.css declares it.Try it Yourself »
Discussion
Loading…