WordPress オリジナルテーマ作成ガイド|環境構築からテンプレートファイル実装まで


WordPressのオリジナルテーマは、style.cssindex.php2ファイルだけで成立します。この2つを wp-content/themes/ の中に作った新しいフォルダに置くと、管理画面の「外観 > テーマ」に自作テーマとして並び、有効化できます(出典: Template Files – WordPress Developer Resources)。

ただし、テーマが一覧に出ることと、自分が書いたCSSやPHPが実際にページへ反映されていることは別の話です。テーマ作成でつまずく人の大半は、コードを書けないのではなく「いま自分がどこまで到達しているのか」を画面で判定できずに止まっています。見た目が変わらないとき、ファイルの置き場所が違うのか、読み込みが通っていないのか、単にブラウザのキャッシュなのかが切り分けられないからです。

この記事では、ローカル環境の構築 → 2ファイルの最小テーマ作成 → 有効化 → ブラウザのDevToolsによる読み込みの検証 → テンプレートファイルの追加、という順で進めます。中心にあるのは4番目の検証です。自作CSSのURLに ?ver= が付いているかどうかという1つの観測だけで、テーマがWordPressの読み込み機構を通っているかを判定できます。

なお本記事が扱うのはクラシックテーマ.php のテンプレートファイルで組み立てるテーマ)です。ブロックテーマとの関係は最初の章で整理します。

WordPress実践ガイド(4段階の現在地を判定する)に戻る


オリジナルテーマとは|公式が必須としているのは2ファイルだけ

オリジナルテーマとは、既存の配布テーマを使わず、自分でテンプレートファイルを書いて作るWordPressテーマのことです。既存テーマの子テーマとは違い、親テーマに依存しないため、出力されるHTMLを自分で完全に決められます。

必須は style.css と index.php|functions.php と screenshot.png は任意

WordPress公式のテーマハンドブックは、クラシックテーマで index.phpstyle.css の2つを required(必須)と明記しています(出典: Template Files – WordPress Developer Resources)。よく「必須ファイルは4つ」と紹介されますが、残る2つは無くてもテーマは動きます。

ファイル必須か無いとどうなるか
style.css必須冒頭のヘッダコメントを読んでテーマ一覧が作られるため、無いと「外観 > テーマ」に出てこない
index.php必須最終フォールバックのテンプレート。無いとテーマとして認識されず、有効化できない
functions.php任意テーマは動く。ただしCSSの読み込みやメニュー登録ができないため、実務ではほぼ必ず作る
screenshot.png任意テーマは動く。管理画面のサムネイルが灰色の枠になるだけ(推奨サイズ 1200×900px)

screenshot.png のサイズと、assets/ / inc/ といったフォルダ名の慣習も公式のテーマ構造ドキュメントに記載があります(出典: Theme Structure – WordPress Developer Resources)。

「認識される」条件と「表示される」条件は別

自作テーマが動かないときの原因は、ほぼこの2つのどちらかに分かれます。混ざったまま調べると原因が絞れないので、最初に分けて覚えてください。

状態満たすべき条件画面で見える症状
テーマとして認識されるstyle.css の冒頭コメントに Theme Name: がある満たさないと「外観 > テーマ」の一覧そのものに出てこない
有効化して表示されるindex.php が存在し、PHPの構文エラーが無い満たさないと有効化できない、または有効化後に白画面になる

WordPressが style.css のヘッダコメントを読み取って管理画面の「外観(テーマ)」に情報を表示することは、公式ドキュメントに明記されています(出典: Main Stylesheet (style.css) – WordPress Developer Resources)。ファイル名も置き場所も正しいのに一覧に出ない場合は、まずこのヘッダを疑うのが最短です。

クラシックテーマとブロックテーマ|2026年時点の現在地

WordPressのテーマには現在2つの方式があります。クラシックテーマ.php のテンプレートファイルでページを組み立てる従来型、ブロックテーマtemplates/ 配下の .html ファイルと theme.json で組み立てる新しい型です。ブロックテーマの必須ファイルは style.csstemplates/index.html の2つで、クラシックテーマとは必須の組み合わせ自体が違います(出典: Theme Structure – WordPress Developer Resources)。

公式ハンドブックは、ブロックテーマについて「このハンドブックは主にこの方式でのテーマ制作を扱う。それがWordPressプロジェクトの将来だからだ」と述べています(原文: “This handbook will primarily focus on building themes using this method because it is the future of the WordPress project.” 出典: What Is a Theme? – WordPress Developer Resources)。テンプレート階層の解説ページ自体も、既定ではブロックテーマ向けの .html 基準に書き換えられました。

一方で、クラシックテーマ向けのドキュメントは公式ハンドブック内に「Classic themes」として現在も維持されており、既存サイトの改修や受託案件では .php ベースのテーマを触る場面が当分残ります。両者は排他ではなく、目的で選ぶものです。

クラシックテーマブロックテーマ
テンプレートの実体index.php(PHP)templates/index.html(HTML+ブロックマークアップ)
必須ファイルstyle.cssindex.phpstyle.csstemplates/index.html
デザイン設定の中心CSSファイルtheme.json とサイトエディター
編集する人開発者(コードエディタ)開発者+運用者(サイトエディター)
向いている場面出力HTMLを完全に握りたい案件・既存テーマの改修運用者が管理画面でレイアウトまで触る前提の新規案件

本記事はここから先、クラシックテーマの作り方を解説します。まず1本のテーマを .php で通しで作ると、テンプレート階層とループというWordPressの中核が身につき、ブロックテーマへ移るときにも同じ知識がそのまま使えます。

既存テーマとオリジナルテーマ、案件でどちらを選ぶかの判断基準


Step 1:ローカル開発環境を構築する(Local)

テーマ開発は本番サーバーで直接行いません。PHPの構文エラーひとつでサイト全体が白画面になるためです。まずは手元のPCにWordPressが動く環境を作ります。

Localとは|WordPress専用のローカル環境ツール

Localは、WordPressのローカル開発環境をGUIだけで用意できるデスクトップアプリです。現在の正式な表記は「Local」(提供元表記は Local by WP Engine)で、公式サイトの名称もこれに統一されています(出典: Local – localwp.com)。古い解説記事では別の名称で紹介されていることがありますが、同じソフトだと読み替えてください。

  • WordPressの自動インストール:サイトを1つ作ると、WordPress本体・PHP・データベースがまとめて用意されます。
  • SSL対応:ローカルでも https で動作確認ができます。
  • PHPバージョンの切り替え:本番サーバーと同じPHPバージョンに合わせて検証できます。
  • サイトの複製:現在の状態をコピーしてから壊れる操作を試せます。

インストールと初期設定

  1. 公式サイトからOS(Windows / Mac / Linux)に合ったインストーラーをダウンロードして、インストールします。
  2. Localを起動し、「Create a New Site」(新しいサイトを作成)をクリックします。
  3. サイト名を入力します。ここで入れた名前がフォルダ名になるので、英数字とハイフンにしておくと後で扱いやすくなります。
  4. 環境設定は「Preferred」(推奨)のままで問題ありません。本番サーバーのPHPバージョンが分かっている場合だけ「Custom」で合わせます。
  5. WordPressのユーザー名・パスワード・メールアドレスを設定し、サイト作成を実行すればセットアップは完了です。

作成後は、サイト一覧の「Open site」でフロント画面、「WP Admin」で管理画面(/wp-admin)を開けます。

テーマを置く wp-content/themes/ の場所を確認する

テーマファイルの置き場所が分からないまま進めると、あとで「編集しているファイルと表示されているファイルが違う」という一番切り分けにくい事故が起きます。最初に実物の場所を開いて確認しておきます。

  1. Localでサイトを選び、「Go to site folder」(サイトフォルダを開く)をクリックします。
  2. 開いたフォルダの中にある app/public/ がWordPressのインストール先です。
  3. その下の app/public/wp-content/themes/ が、テーマを置くフォルダです。既定のテーマ(Twenty Twenty-Five など)が並んでいれば正解です。
  4. ここに新しいフォルダを1つ作ります。フォルダ名がテーマのディレクトリ名になります(例: mytheme)。

パスを暗記する必要はありません。「Go to site folder」から辿るのが確実で、サイトの保存先を既定から変えている場合でも同じ手順で到達できます。


Step 2:2ファイルの最小テーマを作って有効化する

いきなり10個以上のテンプレートファイルを作ると、動かなかったときに原因の候補が多すぎて切り分けられません。まず必須の2ファイルだけで有効化まで到達し、そこから足していきます。

style.css にテーマ情報のヘッダコメントを書く

style.css の冒頭には、テーマ情報を書いたコメントブロックを置きます。WordPressはこの部分を読んでテーマ一覧を組み立てます。

/*
Theme Name: My Theme
Theme URI: https://example.com/mytheme/
Author: Your Name
Author URI: https://example.com/
Description: 自作のクラシックテーマ。
Version: 1.0.0
Requires at least: 6.0
Requires PHP: 7.4
License: GNU General Public License v2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: mytheme
*/

body {
  margin: 0;
  font-family: system-ui, sans-serif;
  line-height: 1.8;
}

このうち必ず必要なのは Theme Name: の1行だけです。他の項目は管理画面の表示や配布時の情報であり、無くてもテーマは認識されます。逆に Theme Name: が無いと、フォルダを正しい場所に置いてもテーマ一覧には現れません(出典: Main Stylesheet (style.css) – WordPress Developer Resources)。

index.php を書く

index.php は、他に該当するテンプレートが無いときに使われる最終フォールバックです。最小構成でも、次の3つのテンプレートタグは最初から入れておきます。

  • wp_head()</head> の直前に置く。プラグインやテーマが読み込むCSS・JavaScript・メタタグはここから出力されます。
  • wp_body_open()<body> の直後に置く。WordPress 5.2で追加されたフックで、Google Tag Managerの <noscript> タグなど body直後に必要な出力に使われます(出典: wp_body_open() – WordPress Developer Resources)。
  • wp_footer()</body> の直前に置く。フッターで読み込むスクリプトや管理バーがここから出力されます。
<?php
/**
 * 最小構成の index.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(); ?>
</head>
<body <?php body_class(); ?>>
<?php wp_body_open(); ?>

<h1><?php bloginfo( 'name' ); ?></h1>

<?php if ( have_posts() ) : ?>
    <?php while ( have_posts() ) : the_post(); ?>
        <article>
            <h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
            <?php the_excerpt(); ?>
        </article>
    <?php endwhile; ?>
<?php else : ?>
    <p>投稿がまだありません。</p>
<?php endif; ?>

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

この3つのどれかを書き忘れると、テーマ自体は動くのに「CSSが読み込まれない」「プラグインが動かない」「管理バーが出ない」といった症状だけが出ます。原因がテーマ側にあると気づきにくいので、最初から雛形に入れておくのが安全です。

「外観 > テーマ」に表示して有効化する

  1. wp-content/themes/ の下に作ったフォルダ(例: mytheme)に、style.cssindex.php を置きます。
  2. 管理画面で「外観 > テーマ」を開きます。
  3. Theme Name: に書いた名前(例: My Theme)のカードが並んでいることを確認します。
  4. そのカードの「有効化」をクリックします。
  5. サイトのトップページを開き、白画面にならずサイト名と投稿一覧が表示されることを確認します。

ここまで到達すれば、テーマは認識され、有効化され、表示されている状態です。カードが並ばない場合はファイルの置き場所ではなく style.css のヘッダコメントを、有効化後に白画面になる場合は index.php のPHP構文エラーを疑ってください。

functions.php を足して style.css を実際に読み込ませる

ここが最初の落とし穴です。クラシックテーマの style.css は、置いただけではページに読み込まれません。この時点ではテーマ情報の宣言に使われているだけで、フロントに配信するにはテーマ側から明示的に読み込む必要があります。

テーマフォルダに functions.php を作り、次の数行を書きます。

<?php
/**
 * 最小構成の functions.php
 * style.css を WordPress の読み込み機構(wp_enqueue_style)経由で出力する。
 */

function mytheme_enqueue_assets() {
    wp_enqueue_style( 'mytheme-style', get_stylesheet_uri() );
}
add_action( 'wp_enqueue_scripts', 'mytheme_enqueue_assets' );

get_stylesheet_uri() は有効化中のテーマの style.css のURLを返す関数です。wp_enqueue_scripts に登録すると、wp_head() が出力する <link> タグとしてページに差し込まれます。これで style.css に書いたCSSが実際に効きます。

ここまでで「おそらく読み込めているはず」の状態になりました。次の章では、これを推測ではなく画面で確定させます。


Step 3:検証|自作CSSが読み込まれているかをDevToolsで判定する

「背景色が変わったから読み込めている」という判断は当てになりません。ブラウザキャッシュが残っていた、別のCSSが後勝ちしていた、そもそも編集していたのは別フォルダのテーマだった —— どれも見た目だけでは区別できないからです。

ブラウザのDevTools(デベロッパーツール)を使うと、この判定は数十秒で終わります。しかも、合否は主観ではなく画面に出ている文字列で決まります。

合否ライン

次の4行がすべて満たされていれば、テーマ開発の土台は完成しています。1行でも欠けたら、その行が今の課題です。

確認すること合格の状態(画面で見えること)見る場所
テーマが認識されている「外観 > テーマ」に自作テーマ名のカードが並ぶ管理画面
テーマが有効化されている有効化後、トップページが白画面にならず表示されるフロント画面
自作CSSが配信されている自作テーマの style.css がステータス 200 で読まれているDevToolsのNetworkタブ
WordPressの読み込み機構を通っているその style.css のURLに ?ver= が付いているDevToolsのNetworkタブ

検証の手順

  1. サイトのトップページをブラウザで開きます。
  2. DevToolsを開きます(Windows: F12 / Mac: Command + Option + I)。
  3. 「Network」タブを選び、フィルタで「CSS」に絞り込みます。
  4. ページを再読み込みします。キャッシュの影響を消すため、再読み込みボタンを長押しして「キャッシュの消去とハード再読み込み」を選ぶと確実です。
  5. 一覧から wp-content/themes/mytheme/style.css の行を探します。
  6. その行の Status が 200 であること、Name(またはURL)の末尾に ?ver= が付いていることを確認します。

ここで見えるURLは、たとえば http://mytheme.local/wp-content/themes/mytheme/style.css?ver=7.0.2 のような形になります。?ver= の後ろの数字はこの時点ではWordPress本体のバージョン番号なので、環境によって変わります。判定に使うのは数字の中身ではなく ?ver= が付いているかどうかです。

なぜ ?ver= が付くのか

wp_enqueue_style()$handle, $src, $deps, $ver, $media という引数を取ります。第4引数 $ver の既定値は false で、省略するとWordPressが現在のバージョン番号をクエリ文字列として自動的に付けます(出典: wp_enqueue_style() – WordPress Developer Resources)。

つまり先ほど書いた3行の functions.php は、$ver を省略しているからこそ ?ver= 付きのURLを出力しています。特別な設定は要りません。

<?php
/**
 * $ver の渡し方3パターンの比較
 */

function mytheme_enqueue_ver_samples() {
    // (1) 省略 … WordPress本体のバージョンが ?ver= として付く
    wp_enqueue_style( 'mytheme-style', get_stylesheet_uri() );

    // (2) 文字列を渡す … 渡した値がそのまま ?ver= になる
    wp_enqueue_style( 'mytheme-main', get_theme_file_uri( '/assets/css/main.css' ), [], '1.0.3' );

    // (3) null を明示 … ?ver= が付かない(CDN側でハッシュを付ける場合など)
    wp_enqueue_style( 'mytheme-vendor', get_theme_file_uri( '/assets/css/vendor.css' ), [], null );
}
add_action( 'wp_enqueue_scripts', 'mytheme_enqueue_ver_samples' );
$ver の渡し方出力されるURL使いどころ
省略(既定値 falsestyle.css?ver=…(WordPress本体のバージョン番号が入る)最小テーマの動作確認。まずはこれで十分
文字列を渡す(例 '1.0.3'style.css?ver=1.0.3テーマのリリース番号を自分で管理する場合
null を明示的に渡すstyle.css(クエリなし)CDNやビルドツール側でファイル名にハッシュを付けている場合

逆に、<link> タグを header.php に直書きしたCSSにはWordPressが一切介在しないため ?ver= は付きません。つまりURLに ?ver= が付いているかどうかだけで、自分のCSSがWordPressの読み込み機構を通っているかを見分けられます。見た目が正しく表示されていても直書きのままだと、子テーマ化・プラグインとの依存解決・読み込み順の制御ができず、機能追加の段階で必ず行き詰まります。

linkタグ直書きと wp_enqueue_style の違いを詳しく読む

合否ラインを外したときの切り分け

外れたときは、次の表で原因を1つに決めてください。原因が決まったらそこで手を止め、対応する記事を1本だけ読んで戻ってくるのが最短です。ここで芋づる式に調べ始めると、元の課題を見失います。

症状(DevToolsで見えること)原因次に読む1本
自作テーマの style.css の行がそもそも出てこないfunctions.phpwp_enqueue_style() を呼んでいない。または index.phpwp_head() が無く、出力先が存在しないCSSを正しく読み込む方法
行はあるが Status が 404パスの指定ミス。get_stylesheet_uri() を使わず自分でURLを組み立てている場合に起きやすいパス指定の徹底解説
Status は 200 だが URLに ?ver= が付いていない<link> タグの直書き。または $vernull を渡しているCSSを正しく読み込む方法
style.css は200で読めているのにスタイルが当たらないあとから読み込まれた別のCSSが上書きしている。または古いCSSがキャッシュされているキャッシュを消してハード再読み込みし、Elementsタブで該当要素の適用CSSを確認する(ここで判定は終わり)

この4行を通過したテーマは、「WordPressがテーマとして認識し、有効化され、テーマのアセットがWordPressの機構経由で配信されている」状態です。以降のテンプレートファイル追加は、この土台の上に積むだけの作業になります。

4段階の到達判定チェックに戻って、次の段階へ進む


Step 4:テンプレートファイルの全体像と役割

土台ができたら、ページの種類ごとにテンプレートファイルを足していきます。index.php だけでも全ページは表示できますが、投稿ページと固定ページと検索結果で出したいHTMLは違うので、役割ごとにファイルを分けます。

ほとんどのテーマで作るファイル

ファイル担当するページ補足
header.php全ページの共通ヘッダーget_header() で読み込む。<!DOCTYPE html> から <body> の開始までを書く
footer.php全ページの共通フッターget_footer() で読み込む。wp_footer() を必ず含める
sidebar.phpサイドバーget_sidebar() で読み込む。ウィジェットエリアの設置に使う
single.php投稿の個別ページ無い場合は singular.php、それも無ければ index.php
page.php固定ページ無い場合は singular.php、それも無ければ index.php
archive.phpカテゴリ・タグ・著者・日付などの一覧より具体的なテンプレートが無いときの一覧の受け皿
search.php検索結果無い場合は index.php
404.phpページが見つからないとき無い場合は index.php

必要になったら作るファイル

ファイル担当するページ
front-page.phpサイトのフロントページ(後述のとおり表示設定に関係なく最優先)
home.php投稿一覧ページ(ブログのインデックス)
category.phpカテゴリ別の一覧
tag.phpタグ別の一覧
author.php著者別の一覧
date.php日付アーカイブ
singular.php投稿と固定ページの共通フォールバック
comments.phpコメントとコメントフォーム

フォルダ構成の目安

ファイルが増えてきたら、テーマフォルダの直下に次の3つを作って整理します。クラシックテーマではこれらのフォルダ名はWordPressに強制されるものではなく、いずれも広く使われている慣習です。

  • template-parts/:ヘッダー内のナビゲーションや投稿カードなど、繰り返し使う部品を切り出す。get_template_part() で読み込みます。
  • assets/:CSS・JavaScript・画像を置く。assets/css/ のように役割ごとに分けます。
  • inc/functions.php から読み込むPHPファイルを置く。functions.php が長くなってきたら機能ごとにここへ切り出します。

assets/inc/ は公式のテーマ構造ドキュメントでも例として挙げられていますが、resources/includes/ など別名でも構いません(出典: Theme Structure – WordPress Developer Resources)。


Step 5:主要テンプレートファイルの実装例

ここからは実際のコードです。以下のコードはすべてPHPの構文チェック(php -l)を通したものを掲載しています。出力する値には esc_url() / esc_html() / esc_attr() といったエスケープ関数を付けています。エスケープは「信頼できない値を安全な形に変換してから出力する」ための処理で、WordPressのセキュリティAPIとして用意されています(出典: Escaping Data – WordPress Developer Resources)。

header.php

HTMLの開始からサイトヘッダーまでを書きます。wp_body_open()<body> の直後に置きます。

<?php
/**
 * 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(); ?>
</head>
<body <?php body_class(); ?>>
<?php wp_body_open(); ?>

<header class="site-header">
    <div class="site-branding">
        <a href="<?php echo esc_url( home_url( '/' ) ); ?>">
            <?php bloginfo( 'name' ); ?>
        </a>
    </div>

    <nav class="main-navigation">
        <?php
        wp_nav_menu( [
            'theme_location' => 'primary',
            'container'      => false,
            'menu_class'     => 'nav-menu',
            'fallback_cb'    => false,
        ] );
        ?>
    </nav>
</header>
  • language_attributes()lang="ja" のような言語属性を出力します。
  • body_class():投稿タイプやテンプレートに応じたクラスを <body> に付けます。CSSの出し分けに使えます。
  • wp_nav_menu():管理画面で作ったメニューを出力します。fallback_cbfalse にしておくと、メニュー未設定時に全固定ページが並ぶ既定動作を止められます。
  • esc_url( home_url( '/' ) ):ホームURLをURLとして安全な形にしてから出力します。

header.php と footer.php の作り方をファイル単位で詳しく読む

footer.php

wp_footer()</body> の直前に必ず置きます。ここが無いと、フッターで読み込む設計のスクリプトやプラグインが動きません。

<?php
/**
 * footer.php
 */
?>
<footer class="site-footer">
    <p>
        &copy; <?php echo esc_html( wp_date( 'Y' ) ); ?>
        <a href="<?php echo esc_url( home_url( '/' ) ); ?>"><?php bloginfo( 'name' ); ?></a>
    </p>
</footer>

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

年号の出力に date() ではなく wp_date() を使っているのは、WordPressの「設定 > 一般」で指定したタイムゾーンに従わせるためです。date() はサーバーのタイムゾーン依存になり、年末年始に1年ずれることがあります。

index.php(ヘッダー・フッターを分離した版)

header.phpfooter.php を切り出したので、index.php は本文部分だけになります。

<?php get_header(); ?>

<main class="site-main">
    <?php if ( have_posts() ) : ?>
        <?php while ( have_posts() ) : the_post(); ?>
            <article <?php post_class(); ?>>
                <h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
                <?php the_excerpt(); ?>
            </article>
        <?php endwhile; ?>

        <?php the_posts_pagination(); ?>
    <?php else : ?>
        <p>投稿が見つかりませんでした。</p>
    <?php endif; ?>
</main>

<?php get_footer(); ?>

have_posts()the_post() の組み合わせがWordPressループです。have_posts() は「まだ表示していない投稿があるか」を判定し、the_post() が次の投稿を現在の投稿として準備します。この2つが揃って初めて the_title()the_content() が正しい投稿の値を返します。

WordPressループの仕組み(have_posts と the_post)を詳しく読む

single.php

投稿の個別ページです。post_class() は投稿IDやカテゴリを含むクラスを出力するので、記事ごとの出し分けに使えます。

<?php get_header(); ?>

<main class="site-main">
    <?php if ( have_posts() ) : ?>
        <?php while ( have_posts() ) : the_post(); ?>
            <article <?php post_class(); ?>>
                <h1><?php the_title(); ?></h1>
                <time datetime="<?php echo esc_attr( get_the_date( 'c' ) ); ?>">
                    <?php echo esc_html( get_the_date() ); ?>
                </time>
                <?php the_content(); ?>
            </article>
        <?php endwhile; ?>
    <?php else : ?>
        <p>投稿が見つかりませんでした。</p>
    <?php endif; ?>
</main>

<?php get_footer(); ?>

日付は get_the_date( 'c' ) で機械可読なISO 8601形式を datetime 属性に、get_the_date() で表示用の文字列を本文に出しています。属性値には esc_attr()、本文には esc_html() と使い分けます。

page.php

固定ページ用です。single.php とほぼ同じ形ですが、投稿日やカテゴリのような投稿向けのメタ情報は出さないのが一般的です。

<?php get_header(); ?>

<main class="site-main">
    <?php if ( have_posts() ) : ?>
        <?php while ( have_posts() ) : the_post(); ?>
            <article <?php post_class(); ?>>
                <h1><?php the_title(); ?></h1>
                <?php the_content(); ?>
            </article>
        <?php endwhile; ?>
    <?php endif; ?>
</main>

<?php get_footer(); ?>

404.php

該当するページが無いときのテンプレートです。「見つかりません」で終わらせず、検索フォームとトップページへの導線を置いて離脱を減らします。

<?php get_header(); ?>

<main class="site-main">
    <h1>ページが見つかりません</h1>
    <p>お探しのページは存在しないか、移動した可能性があります。</p>

    <?php get_search_form(); ?>

    <a href="<?php echo esc_url( home_url( '/' ) ); ?>">トップページへ戻る</a>
</main>

<?php get_footer(); ?>

functions.php

テーマの機能を宣言するファイルです。テンプレートタグと違い、ここに書いた内容は全ページで実行されます。

<?php
/**
 * 実用的な functions.php
 */

function mytheme_setup() {
    add_theme_support( 'title-tag' );
    add_theme_support( 'post-thumbnails' );
    add_theme_support( 'html5', [ 'search-form', 'comment-form', 'comment-list', 'gallery', 'caption', 'style', 'script' ] );
    register_nav_menus( [
        'primary' => 'メインメニュー',
    ] );
}
add_action( 'after_setup_theme', 'mytheme_setup' );

function mytheme_enqueue_scripts() {
    // 第4引数を省略しているので ?ver= にはWordPress本体のバージョンが入る
    wp_enqueue_style( 'mytheme-style', get_stylesheet_uri() );

    // 自分で更新するCSSは、ファイルの更新時刻をバージョンに使うとキャッシュ事故を防げる
    $main_css = get_theme_file_path( '/assets/css/main.css' );
    if ( file_exists( $main_css ) ) {
        wp_enqueue_style(
            'mytheme-main',
            get_theme_file_uri( '/assets/css/main.css' ),
            [ 'mytheme-style' ],
            (string) filemtime( $main_css )
        );
    }
}
add_action( 'wp_enqueue_scripts', 'mytheme_enqueue_scripts' );
  • add_theme_support( 'title-tag' )<title> タグをWordPressに任せます。テーマ側で <title> を書く必要がなくなります。
  • add_theme_support( 'post-thumbnails' ):アイキャッチ画像を使えるようにします。
  • register_nav_menus():管理画面の「外観 > メニュー」で編集できるメニュー位置を登録します。
  • 2つ目の wp_enqueue_style() では filemtime() をバージョンに使っています。ファイルを更新するたびにURLが変わるため、ブラウザキャッシュが原因の「直したのに反映されない」を防げます。

functions.php で使える実践的なコードスニペット集を読む


テンプレート階層の仕組みと front-page.php の落とし穴

テンプレート階層(Template Hierarchy)とは、表示しようとしているページの種類に応じて、WordPressがどのテンプレートファイルを使うかを上から順に探していく仕組みのことです。ファイル名だけで適用先が決まるため、設定画面での紐付け作業は要りません。

カテゴリページの例で見る探索順

たとえば news というスラッグのカテゴリページを開いたとき、WordPressは次の順でファイルを探し、最初に見つかった1つを使います。

  1. category-news.php(このカテゴリ専用)
  2. category-4.php(カテゴリIDで指定)
  3. category.php(全カテゴリ共通)
  4. archive.php(アーカイブ共通)
  5. index.php(最終フォールバック)

この仕組みが分かると、「特定のカテゴリだけレイアウトを変えたい」という要望に、条件分岐を書かずファイルを1つ足すだけで応えられます。

表示するページ最優先のテンプレートフォールバックの順序
フロントページfront-page.phphome.php または page.phpindex.php
投稿の個別ページsingle-{post_type}.phpsingle.phpsingular.phpindex.php
固定ページpage-{slug}.phppage.phpsingular.phpindex.php
カテゴリ一覧category-{slug}.phpcategory.phparchive.phpindex.php
検索結果search.phpindex.php
404エラー404.phpindex.php

front-page.php は表示設定に関係なく常に優先される

ここが、テーマ制作の解説で最も誤って伝わっている箇所です。「設定 > 表示設定でフロントページを固定ページにすると front-page.php が使われる」という説明を見かけますが、これは正確ではありません

公式ドキュメントはこう書いています。“The front-page.php template file is used to render your site’s front page, whether the front page displays the blog posts index (mentioned above) or a static page.”(出典: Template Hierarchy – WordPress Developer Resources)。つまり front-page.php は、「最新の投稿」設定でも「固定ページ」設定でも、存在すれば必ず優先されますhome.php は無視されます。

これが引き起こす事故は独特です。PHPエラーが出ないため、壊れているように見えません。「最新の投稿」設定のサイトに、固定ページ表示を前提とした front-page.php(ループを書かず the_title()the_content() だけを呼ぶ形)を置くと、投稿一覧が消えて最新の投稿1件の中身だけが表示されます。テンプレートは動いているので、原因を home.php 側に探しに行っても何も見つかりません。

対処は2つあります。フロントページの表示方法を変える可能性があるサイトでは、front-page.php の中で is_page() による分岐を書いておくのが安全です。フロントページに専用デザインが要らないなら、front-page.php を作らないという選択がいちばん確実です。

<?php get_header(); ?>

<main class="front-page">
    <?php if ( is_page() ) : ?>

        <?php /* 「ホームページの表示」が固定ページのとき */ ?>
        <section class="hero">
            <h1><?php the_title(); ?></h1>
            <?php the_content(); ?>
        </section>

    <?php else : ?>

        <?php /* 「ホームページの表示」が最新の投稿のとき */ ?>
        <section class="posts">
            <h1><?php bloginfo( 'name' ); ?></h1>
            <?php if ( have_posts() ) : ?>
                <?php while ( have_posts() ) : the_post(); ?>
                    <article <?php post_class(); ?>>
                        <h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
                        <?php the_excerpt(); ?>
                    </article>
                <?php endwhile; ?>
            <?php else : ?>
                <p>投稿がまだありません。</p>
            <?php endif; ?>
        </section>

    <?php endif; ?>
</main>

<?php get_footer(); ?>

なお、固定ページ表示のときにループを書かずに the_title()the_content() だけで動くのは、WordPressがこれらの関数の内部で現在の投稿データを組み立て直すためです。動作としては正しいので、コードが間違っているのではなく、どの設定でこのファイルが呼ばれるかの理解が間違っているという種類の不具合になります。


テンプレートファイル別の詳細ガイド

この記事はテーマ1本を通しで作る流れを扱いました。個々のファイルの書き方をもっと深く知りたくなったら、以下の記事にそれぞれ1ファイル分の解説があります。


よくある質問(FAQ)

Q. WordPressテーマの最低限必要なファイルは何ですか?

style.cssindex.php の2ファイルです。公式のテーマハンドブックがこの2つを required と明記しています。functions.phpscreenshot.png は任意で、無くてもテーマは認識され有効化できます。ただし functions.php が無いとCSSの読み込みやメニュー登録ができないため、実務ではほぼ必ず作ります。

Q. 自作テーマが「外観 > テーマ」に表示されません。何を疑えばよいですか?

style.css 冒頭のコメントに Theme Name: の行があるかを最初に確認してください。WordPressはこのヘッダを読んでテーマ一覧を組み立てるため、ヘッダが無いフォルダはファイル名と置き場所が正しくてもテーマとして認識されません。次に疑うのは置き場所で、wp-content/themes/ の直下にテーマフォルダがあるか(wp-content/themes/twentytwentyfive/ の中に入れ子になっていないか)を確認します。

Q. CSSを書き換えても表示が変わりません。原因の切り分け方を教えてください。

DevToolsのNetworkタブで自作テーマの style.css を探し、(1) 行が存在するか (2) Statusが200か (3) URLに ?ver= が付いているか、の3点を上から順に見てください。行が無ければ wp_enqueue_style() での読み込み自体が無い、404ならパスの誤り、?ver= が無ければ <link> タグの直書きです。3点すべて通っているのに変わらない場合はブラウザキャッシュか他のCSSによる上書きなので、キャッシュを消してハード再読み込みしてから再判定します。

Q. front-page.php を置いたらブログの投稿一覧が消えました。なぜですか?

front-page.php は「設定 > 表示設定」の内容に関係なく、存在すれば常に最優先で使われるためです。「最新の投稿」設定のままでも front-page.php が呼ばれ、home.php は無視されます。ループを書いていない front-page.php だと投稿一覧が出力されないので、一覧が消えたように見えます。is_page() による分岐を入れるか、front-page.php 自体を作らないでください。

Q. functions.phpとプラグインの違いは何ですか?

functions.php はテーマに属するファイルなので、テーマを切り替えると書いた機能ごと無効になります。プラグインはテーマから独立しているため、テーマを変えても機能が残ります。見た目や出力HTMLに直結する機能は functions.php、テーマを変えても残したい機能(お問い合わせフォーム、SEO設定、カスタム投稿タイプなど)はプラグインに置くのが基本的な分け方です。

Q. これから作るならクラシックテーマとブロックテーマのどちらを選ぶべきですか?

運用者が管理画面でレイアウトまで編集する前提の新規案件ならブロックテーマ、出力HTMLを完全に握りたい案件や既存のクラシックテーマの改修ならクラシックテーマです。公式ハンドブックはブロックテーマを「WordPressプロジェクトの将来」と位置づけていますが、クラシックテーマ向けのドキュメントも現在も維持されています。学習目的であれば、テンプレート階層とループを直接扱うクラシックテーマを1本作っておくと、ブロックテーマに移ったあとも知識がそのまま使えます。

Q. 子テーマを使うべきですか?

既存の配布テーマをカスタマイズする場合は子テーマを使ってください。親テーマのアップデートでカスタマイズが上書きされるのを防げます。一方、この記事のようにゼロからオリジナルテーマを作る場合は子テーマは不要です。自分のテーマが更新で上書きされることはないためです。

Q. テーマ開発にPHPの知識はどのくらい必要ですか?

変数、if による条件分岐、while によるループ、関数呼び出しの4つが読めれば着手できます。テーマ制作で書くPHPの大半は the_title()the_content() といったWordPressのテンプレートタグを呼ぶだけで、自分でクラスや複雑なロジックを設計する場面は最初のうちほとんどありません。むしろ the_get_the_ の使い分けのような、WordPress固有のルールを覚えるほうが先です。


まとめ

WordPressオリジナルテーマの制作は、次の順で進めると原因の切り分けができる状態を保てます。

  1. Localでローカル環境を作り、wp-content/themes/ の実物の場所を開いて確認する。
  2. style.cssindex.php の2ファイルだけのフォルダを置き、「外観 > テーマ」に出して有効化する。
  3. functions.phpstyle.css を読み込み、DevToolsのNetworkタブでステータス200と ?ver= を確認する
  4. header.php / footer.php / single.php / page.php と、必要なテンプレートファイルを足していく。

3番目の検証を飛ばさないことが、この記事でいちばん伝えたい点です。?ver= が付いていることを一度自分の目で確認しておくと、以降「反映されない」という現象に出会ったときに、原因がテーマの読み込みなのかキャッシュなのかを毎回1分で切り分けられるようになります。

テンプレート階層まで理解できたら、次は functions.php から機能を足していく段階です。管理画面で入力した値をフロントに出せるようになると、テーマは「見た目を作るもの」から「サイトの仕組みを作るもの」に変わります。

WordPress実践ガイドで次の段階(機能カスタマイズ)へ進む