テクニック

WordPress子テーマの作り方|最小構成とfunctionsの書き方

この記事の対象: クラシックテーマをカスタマイズする制作者・これから子テーマを作る人
読了時間: 約12分

WordPressの子テーマは、ファイルを2つ置けば有効化できます。ただし「有効化できる」ことと「正しく動く」ことは別です。親テーマのCSSを読み込む書き方ひとつ取っても、古い時代の記述がいまだにコピペで広まっていて、そのまま使うと読み込み順が崩れたり、二重読み込みが起きたりします。

この記事では、style.css と functions.php の最小構成、親テーマのスタイルを読み込む正しい書き方、テンプレートを上書きするときのファイルの置き場所を、コピーしてそのまま動くコードで説明します。「なぜその書き方なのか」まで踏み込むので、手元のテーマに合わせて判断できるようになります。


前提の確認:ブロックテーマかクラシックテーマか

前提の確認:ブロックテーマかクラシックテーマか

作業に入る前に、対象のテーマがどちらかを確定させてください。子テーマの作り方が変わります。

クラシックテーマ ブロックテーマ(FSE)
判別方法 テーマ直下に theme.json が無い/index.php がある テーマ直下に templates/ フォルダと theme.json がある
子テーマの中身 style.css + functions.php + 上書きするPHPテンプレート style.css + theme.json + templates/
見た目の編集 外観 → テーマファイルエディター/コード 外観 → エディター(サイトエディター)

この記事が扱うのはクラシックテーマです。国内の制作案件で使われる配布テーマにはクラシック構造のものがまだ多く、子テーマの需要もここに集中しています。ブロックテーマの場合は子テーマを作らずに「スタイル」からCSSを追加できる場面が多く、判断そのものが変わります。

判別に迷ったら、FTPやサーバーのファイルマネージャーで wp-content/themes/親テーマ名/ を開き、theme.json があるか見てください。あればブロックテーマ、無ければクラシックテーマです。

必要な環境と権限

  • WordPress 5.0以降(この記事のコードは 6.x 系で動作を確認しています)
  • wp-content/themes/ にディレクトリを作成できる権限(FTP/SFTP/サーバー管理画面のファイルマネージャーのいずれか)
  • 管理画面の「管理者」権限
  • 作業前のバックアップ(本番で直接やる場合は必須)

管理画面の「テーマファイルエディター」だけでも子テーマの編集はできますが、新規作成はできません。最初の2ファイルはFTPかファイルマネージャーで置く必要があります。

テーマ選定そのものから迷っている場合は、こちらの記事で判断基準を整理しています。


子テーマの最小構成は2ファイルだけ

子テーマの最小構成は2ファイルだけ

必要なのは style.cssfunctions.php の2つです。それ以外は後から必要になったときに足します。

子テーマの最小構成と置き場所

wp-content/themes/

  • 親テーマフォルダ(触らない)
  • 親テーマ名-child/

親テーマ名-child/(新規作成)

  • style.css(必須)
  • functions.php(必須)
  • screenshot.png(任意)

親テーマのフォルダには一切手を入れません。更新で消えます。

親テーマのフォルダ内のファイルは、テーマがアップデートされると上書きされます。直接編集した内容は消えるため、子テーマ側に置き換えるのが原則です。

ステップ1:フォルダを作る

wp-content/themes/ の直下に、子テーマ用のフォルダを作ります。名前は自由ですが、親テーマ名に -child を付けるのが慣例です。

wp-content/themes/parent-theme-child/

変更する箇所: parent-theme の部分を親テーマのフォルダ名に置き換えてください。表示名ではなくフォルダ名です。管理画面に「My Theme」と出ていても、実際のディレクトリ名が my-theme なら後者を使います。半角スペースや大文字が入っていないか、実物を見てから決めてください。

以降のサンプルコードでは、親テーマのフォルダ名を parent-theme、子テーマのフォルダ名を parent-theme-child として書きます。自分の環境の名前に読み替えてください。

ステップ2:style.css を作る

作ったフォルダの中に style.css を置きます。

ファイルパス: wp-content/themes/parent-theme-child/style.css

/*
Theme Name: Parent Theme Child
Theme URI: https://example.com/
Description: Parent Theme の子テーマです。
Author: 制作者名
Author URI: https://example.com/
Template: parent-theme
Version: 1.0.0
Text Domain: parent-theme-child
*/

/* ここから下にカスタムCSSを書きます */

変更する箇所は3つです。

項目 内容 注意点
Theme Name 管理画面に表示される名前 任意。他のテーマと重複しない名前にする
Template 親テーマのフォルダ名 ここだけは絶対に正確に。大文字小文字も一致させる
Author / Theme URI 制作者情報 任意。空でも動く

Template が間違っていると、管理画面のテーマ一覧に「壊れています」と表示されるか、そもそも一覧に出てきません。表示名を書いてしまう事故が一番多いポイントです。

コメント(/* */)の中に書くのは仕様です。WordPressはこのコメントブロックをテーマ情報として読み取ります。閉じ忘れるとCSS全体が壊れるので、*/ があるか確認してください。

ステップ3:functions.php を作る

同じフォルダに functions.php を置きます。中身は次のセクションで詳しく扱いますが、まずは最小の形です。

ファイルパス: wp-content/themes/parent-theme-child/functions.php

<?php
/**
 * 子テーマの機能ファイル
 */

// 親テーマのスタイルを読み込む
add_action( 'wp_enqueue_scripts', 'child_theme_enqueue_styles' );
function child_theme_enqueue_styles() {
    wp_enqueue_style(
        'child-style',
        get_stylesheet_uri(),
        array( 'parent-style' ),
        wp_get_theme()->get( 'Version' )
    );
}

このコードには意図的に穴があります。'parent-style' というハンドルが本当に存在するかを確かめていない点です。次のセクションで埋めます。

注意: functions.php の1行目は必ず <?php です。その前に空白や改行、BOMが入ると「Headers already sent」というエラーが出て画面が真っ白になります。閉じタグ ?> はファイル末尾に書かないのが定石です。書くとその後ろの改行が出力に混ざります。

ステップ4:有効化する

管理画面 → 外観 → テーマ を開くと、子テーマがカードで表示されます。「有効化」を押してください。

screenshot.png(1200×900px推奨)をフォルダに置くと、テーマ一覧にサムネイルが出ます。無くても動作には影響しませんが、複数案件を回すなら入れておくと取り違えが減ります。


親テーマのCSSを読み込む「正しい」書き方

親テーマのCSSを読み込む「正しい」書き方

ここが本題です。ネット上には3つの流派が混在していて、うち2つは今では推奨されません。

親テーマCSSの読み込み方の比較

推奨:親のハンドルに依存させる

  • 親がどう読み込んでいても壊れない
  • 読み込み順が確実に親→子になる
  • 二重読み込みが起きない

非推奨:@import で読む

  • CSSの直列読み込みで表示が遅れる
  • 親のCSSが1本とは限らない
  • 公式ドキュメントで非推奨扱い

@import は旧WordPress Codexに載っていた方法で、いまだにコピペで広まっています。CSSファイルの中から別のCSSを読むため、ブラウザは親CSSの取得を待ってから子CSSを解析します。読み込みが直列になり、表示が遅れます。

なぜ get_template_directory_uri() だけでは足りないのか

次のコードが「よく見るけど正しくない」書き方の代表です。

// ⚠ この書き方は避ける
function child_theme_enqueue_styles() {
    wp_enqueue_style( 'parent-style', get_template_directory_uri() . '/style.css' );
    wp_enqueue_style( 'child-style', get_stylesheet_uri(), array( 'parent-style' ) );
}
add_action( 'wp_enqueue_scripts', 'child_theme_enqueue_styles' );

問題は2点あります。

1つめ。親テーマの style.css が実質空のことがある。 最近のテーマは、テーマ情報のコメントだけを style.css に置き、実際のスタイルは assets/css/ 配下の複数ファイルに分けています。この書き方だと、中身の無いファイルを1本余計に読み込むだけで終わります。

2つめ。二重読み込みになる。 親テーマが自分で style.csswp_enqueue_style() している場合、ハンドル名が違えば同じファイルが2回読まれます。ハンドル名が偶然一致すれば重複は避けられますが、それは「偶然」に依存しています。

推奨する書き方

親テーマがすでに登録しているハンドルに、子テーマのCSSを依存させるのが確実です。

ファイルパス: wp-content/themes/parent-theme-child/functions.php

<?php
/**
 * 子テーマの機能ファイル
 */

add_action( 'wp_enqueue_scripts', 'child_theme_enqueue_styles', 20 );
function child_theme_enqueue_styles() {

    // 親テーマが登録しているスタイルのハンドル名
    // ここを実際のテーマに合わせて書き換える
    $parent_handle = 'parent-theme-style';

    // 親が本当にそのハンドルで登録しているか確認する
    $deps = wp_style_is( $parent_handle, 'registered' ) ? array( $parent_handle ) : array();

    wp_enqueue_style(
        'child-style',
        get_stylesheet_uri(),
        $deps,
        wp_get_theme()->get( 'Version' )
    );
}

変更する箇所$parent_handle の1行だけです。親テーマがどのハンドル名でCSSを登録しているかを調べて入れてください(調べ方は次項)。

このコードのポイントを分解します。

記述 意味
add_action( ..., 20 ) 優先度20。デフォルトの10より後に実行され、親テーマの登録が済んだ後に走る
wp_style_is( $handle, 'registered' ) そのハンドルが登録済みか確認。無ければ依存を空にして、存在しない依存で読み込みが止まるのを防ぐ
array( $parent_handle ) 第3引数の依存指定。指定したハンドルよりに出力されることが保証される
wp_get_theme()->get( 'Version' ) style.css の Version: の値をクエリ文字列に付ける。キャッシュ対策

第4引数のバージョンを省略すると、WordPress本体のバージョンが自動で付きます。子テーマのCSSを更新してもURLが変わらないため、ブラウザキャッシュが残り続けます。style.css の Version: 1.0.01.0.1 に上げるだけで新しいURLになる、この形にしておくのが実務的です。

親テーマのハンドル名の調べ方

推測せずに実物を見ます。方法は2つあります。

方法1:ソースを検索する

親テーマのフォルダで functions.php を開き、wp_enqueue_style を検索します。第1引数がハンドル名です。

// 親テーマの functions.php にこう書いてあれば
wp_enqueue_style( 'parent-theme-style', get_template_directory_uri() . '/assets/css/style.min.css' );
// ハンドル名は 'parent-theme-style'

条件分岐の中に複数の wp_enqueue_style() が並んでいることもあります。その場合は、どのページでも共通で走っているものを選んでください。

方法2:出力されたHTMLを見る

サイトを表示してソースを見ると、<link> タグの id 属性に -css が付いた形でハンドル名が出ています。

<link rel="stylesheet" id="parent-theme-style-css" href="https://example.com/wp-content/themes/parent-theme/assets/css/style.min.css?ver=1.2.3" media="all">

この場合、id から -css を除いた parent-theme-style がハンドル名です。方法2のほうが確実で速いので、こちらを推奨します。条件分岐で出し分けているテーマでは、トップページと下層ページの両方でソースを見比べると、共通して出ているハンドルが分かります。

親のハンドルが分からない・特定できない場合

複数のCSSを条件分岐で出しているなど、ハンドルを1つに絞れないケースもあります。その場合は、優先度を大きくして「最後に読ませる」形で妥協します。

<?php
add_action( 'wp_enqueue_scripts', 'child_theme_enqueue_styles', 9999 );
function child_theme_enqueue_styles() {
    wp_enqueue_style(
        'child-style',
        get_stylesheet_uri(),
        array(),
        wp_get_theme()->get( 'Version' )
    );
}

優先度9999は「かなり後」という意味で、他のプラグインやテーマの登録処理より後に走ります。ただし依存関係を明示していないので、順序の保証は依存指定ほど強くありません。ハンドルが特定できるなら依存指定を優先してください。

CSSが効かないときに !important を足す前に

子テーマのCSSが親テーマに負けるとき、多くの人は !important を付けて解決します。それでも動きますが、あとで別の上書きが必要になったとき詰みます。

先に確認すべきは次の順です。

  1. 読み込み順 — ブラウザの検証ツールでソースを見て、子テーマのCSSが親より後に出ているか
  2. 詳細度(セレクタの強さ) — 親が .header .nav a と書いているなら、子で .nav a と書いても負ける
  3. セレクタの一致 — 実際に当たっている要素かどうか、検証ツールで確認する

読み込み順が正しくても効かないなら、原因は詳細度です。親と同じか、それより1段強いセレクタを書けば !important は不要になります。


テンプレートを上書きするときの置き場所

テンプレートを上書きするときの置き場所

CSSだけで足りなければ、PHPテンプレート自体を上書きします。ここにも明確なルールがあります。

基本ルール:同じ相対パスに同名で置く

WordPressは、テンプレートファイルを探すときに子テーマを先に見ます。子テーマに無ければ親テーマを見ます。

テンプレートファイルの探索順
1. 子テーマを探すあれば子テーマのファイルを使う
2. 親テーマを探す子に無ければ親のファイルを使う
3. 汎用テンプレートどちらにも無ければ index.php

つまり、親テーマの single.php を上書きしたいなら、親からコピーして子テーマの直下に置くだけです。

親: wp-content/themes/parent-theme/single.php
子: wp-content/themes/parent-theme-child/single.php   ← ここにコピー

サブディレクトリにある場合も、相対パスをそのまま再現します。

親: wp-content/themes/parent-theme/template-parts/content-page.php
子: wp-content/themes/parent-theme-child/template-parts/content-page.php

template-parts フォルダを子テーマ側にも作り、その中に置きます。フォルダ構造を平らにして直下に置くと読まれません。

この規則が効かない例外

上書きが効かないパターンが2つあります。ここでハマる人が多いところです。

1つめ:get_template_part() 以外で読み込まれているファイル

親テーマが includerequire で直接読んでいるファイルは、子テーマに同名で置いても差し替わりません。get_template_part()locate_template() を経由している場合のみ、子テーマが優先されます。

親テーマ側のコードを確認してください。

// これは子テーマで上書きできる
get_template_part( 'template-parts/content', 'page' );

// これは上書きできない(親のファイルが直接読まれる)
require get_template_directory() . '/inc/custom-header.php';

get_template_directory()親テーマのパスを返します。get_stylesheet_directory() が子テーマのパスです。この2つの違いを押さえておくと、上書き可否がコードを読んだだけで判断できます。

関数 返すパス 使う場面
get_template_directory() 親テーマのディレクトリ 親のファイルを明示的に読みたいとき
get_stylesheet_directory() 子テーマのディレクトリ 子テーマ内のファイルを読むとき
get_template_directory_uri() 親テーマのURL 親の画像・CSSのURLが欲しいとき
get_stylesheet_directory_uri() 子テーマのURL 子の画像・CSSのURLが欲しいとき

子テーマの functions.php で自作ファイルを読むときは get_stylesheet_directory() です。ここで get_template_directory() と書くと、親テーマのフォルダを探しに行って「ファイルが無い」で落ちます。なお、親テーマを直接有効化しているサイトではこの2つが同じ値を返すため、テストでは問題が出ず、子テーマを有効化した瞬間に落ちます。書き間違いに気づきにくい典型です。

2つめ:functions.php は上書きではなく「追加」

他のテンプレートと違い、functions.php だけは挙動が逆です。子テーマの functions.php先に読み込まれ、そのあと親テーマの functions.php が読み込まれます。置き換えではなく両方が動きます。

親テーマの関数を「消したい」場合は、コピーして書き換えるのではなく、フックを外す形にします。

<?php
// 親テーマが追加している処理を無効化する例
add_action( 'after_setup_theme', 'child_remove_parent_hooks' );
function child_remove_parent_hooks() {
    // 第2引数の関数名・第3引数の優先度は、親の add_action と完全に一致させる
    remove_action( 'wp_footer', 'parent_theme_footer_script', 10 );
}

変更する箇所'wp_footer'(フック名)、'parent_theme_footer_script'(関数名)、10(優先度)の3つです。親テーマの add_action() の記述と完全に一致していないと外れません。優先度を省略すると10として扱われるため、親が 20 で登録していれば外れずに残ります。

親テーマがクラスのメソッドをフックしている場合は、関数名の文字列ではなく array( $instance, 'method' ) の形で登録されているため、同じインスタンスを取得できないと外せません。テーマがインスタンスをグローバル変数やシングルトンで公開していればそこから取り、公開していなければ remove_action() では外せないと判断して、出力側のフィルターで打ち消す方向に切り替えます。

親が after_setup_theme より前にフックを登録している場合は、remove_action() を呼ぶタイミングを調整する必要があります。動かないときは initwp_loaded に変えて試してください。

関数名の衝突を避ける

子テーマの functions.php が先に読まれるため、親テーマと同じ関数名を定義するとエラーになります(「Cannot redeclare function」)。

子テーマ側の関数には、必ず独自の接頭辞を付けてください。

<?php
// ✕ 衝突する可能性が高い
function setup_theme() {}

// ○ 接頭辞で衝突を避ける
function ptchild_setup_theme() {}

接頭辞は子テーマ名の短縮形で構いません。3〜4文字あれば実用上は十分です。関数だけでなく、定数・グローバル変数・カスタム投稿タイプのスラッグ・オプション名にも同じ接頭辞を通しておくと、あとからプラグインを足したときの衝突も避けられます。

プラガブル関数だけは例外的に置き換えられる

親テーマが if ( ! function_exists( 'xxx' ) ) で囲んで定義している関数は、子テーマで同名の関数を定義すると置き換わります。子テーマが先に読まれるため、親側の function_exists() が真になり、親の定義がスキップされるからです。

// 親テーマ側の書き方
if ( ! function_exists( 'parent_theme_entry_meta' ) ) {
    function parent_theme_entry_meta() {
        // 親の実装
    }
}

この形になっている関数なら、子テーマの functions.php に同名で書けば差し替えられます。親テーマのコードを検索して function_exists があるか見てみてください。差し替えるときは、親の実装を丸ごとコピーしてから必要な箇所だけ変えるのが安全です。中で使われているテーマ独自の関数や変数を落とすと、そこで落ちます。


そのまま使える functions.php のテンプレート

そのまま使える functions.php のテンプレート

ここまでの内容をまとめた、実務で使える形です。コピーして $parent_handle と接頭辞だけ書き換えれば動きます。

ファイルパス: wp-content/themes/parent-theme-child/functions.php

<?php
/**
 * Child Theme - functions.php
 *
 * 変更する箇所:
 *   1. 関数の接頭辞 ptchild_ を自分のテーマ名に
 *   2. $parent_handle を親テーマの実際のハンドル名に
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * スタイルシートの読み込み
 */
add_action( 'wp_enqueue_scripts', 'ptchild_enqueue_assets', 20 );
function ptchild_enqueue_assets() {

    $parent_handle = 'parent-theme-style';
    $deps = wp_style_is( $parent_handle, 'registered' ) ? array( $parent_handle ) : array();

    // 子テーマの style.css
    wp_enqueue_style(
        'ptchild-style',
        get_stylesheet_uri(),
        $deps,
        wp_get_theme()->get( 'Version' )
    );

    // 子テーマ独自のJS(不要ならこのブロックごと削除)
    $js_path = get_stylesheet_directory() . '/assets/js/main.js';
    if ( file_exists( $js_path ) ) {
        wp_enqueue_script(
            'ptchild-script',
            get_stylesheet_directory_uri() . '/assets/js/main.js',
            array(),
            filemtime( $js_path ),
            true
        );
    }
}

JSのブロックでは filemtime() をバージョンに使っています。ファイルの更新日時(UNIXタイムスタンプ)が入るため、JSを保存し直すだけでURLが変わり、キャッシュが自動で切れます。CSSのほうは Version: の手動更新にしていますが、こちらも filemtime( get_stylesheet_directory() . '/style.css' ) に置き換えられます。案件の運用に合わせて選んでください。手で上げるのを忘れる現場なら filemtime()、リリース単位でバージョンを揃えたい現場なら Version: の手動更新が向きます。

file_exists() で囲んでいるのは、ファイルが無いときに filemtime() が警告を出すからです。ファイルを必ず置くなら不要ですが、テンプレートとして配る前提なら残しておくほうが安全です。

冒頭の if ( ! defined( 'ABSPATH' ) ) exit; は、ファイルへの直接アクセスを弾く定型句です。WordPressを経由せずにPHPファイルを叩かれたときにエラー内容が漏れるのを防ぎます。

実務でよく足す3つのスニペット

子テーマを作る場面で書く頻度が高いものを挙げます。上のテンプレートの末尾に追記して使ってください。

1. 管理バーの余計な項目を消す

add_action( 'wp_before_admin_bar_render', 'ptchild_clean_admin_bar' );
function ptchild_clean_admin_bar() {
    global $wp_admin_bar;
    $wp_admin_bar->remove_menu( 'wp-logo' );
    $wp_admin_bar->remove_menu( 'comments' );
}

変更する箇所: 消したい項目のIDを remove_menu() に追加します。IDは管理バーのHTMLの id="wp-admin-bar-xxx" から wp-admin-bar- を除いた部分です。クライアントに渡すサイトで、使わないメニューを畳んでおくと問い合わせが減ります。

2. 投稿者アーカイブを無効化する

ユーザー名が推測される経路を1つ減らします。

add_action( 'template_redirect', 'ptchild_disable_author_archive' );
function ptchild_disable_author_archive() {
    if ( is_author() ) {
        wp_safe_redirect( home_url( '/' ), 301 );
        exit;
    }
}

投稿者ページを使っているサイトでは入れないでください。オウンドメディアで著者情報を出す構成なら、この処理は不要です。

3. 絵文字用のスクリプトを止める

日本語サイトでは使わないことが多く、読み込みを1本減らせます。

add_action( 'init', 'ptchild_disable_emoji' );
function ptchild_disable_emoji() {
    remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
    remove_action( 'wp_print_styles', 'print_emoji_styles' );
    remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
    remove_action( 'admin_print_styles', 'print_emoji_styles' );
}

優先度の 7 は WordPress 本体が登録している値です。ここを省略すると外れません。前述の「remove_action() は完全一致」の実例そのものです。

子テーマに置くか、プラグインに置くかの線引き

functions.php は何でも書ける場所なので、放っておくと数百行に膨らみます。移す基準を先に決めておくと迷いません。

処理の性質 置き場所
テーマの見た目・テンプレート出力に関わる 子テーマの functions.php
テーマを変えても残したい機能(カスタム投稿タイプ、ショートコード) プラグイン
サイト運用に必須で、無効化されると困る wp-content/mu-plugins/
特定案件だけの一時的な調整 子テーマの functions.php

判断に迷ったら「テーマを別のものに差し替えたとき、この処理は消えていいか」で考えてください。消えて困るならテーマの外に出します。

functions.php が膨らんできたら分割する

100行を超えたあたりから、機能ごとにファイルを分けるほうが読みやすくなります。

// 子テーマの functions.php 末尾
foreach ( array( 'enqueue', 'admin', 'cleanup' ) as $ptchild_inc ) {
    $ptchild_file = get_stylesheet_directory() . '/inc/' . $ptchild_inc . '.php';
    if ( file_exists( $ptchild_file ) ) {
        require_once $ptchild_file;
    }
}

inc/enqueue.phpinc/admin.phpinc/cleanup.php を子テーマ内に作り、そこへ移します。ここでも get_stylesheet_directory() を使う点は変わりません。get_template_directory() と書くと親テーマの inc/ を探しに行き、ファイルが無くて落ちます。

外部ライブラリを足さずに済むところは、素のPHPとWordPress標準関数で書くほうが結果的に軽くなります。プラグインを1つ入れるより、functions.php に5行書くほうが速い場面は多いです。


うまくいかないとき

うまくいかないとき

作業中に詰まりやすいポイントを、症状から引ける形でまとめます。

テーマ一覧に子テーマが出てこない

style.css のコメントブロックが読めていません。次の順で確認します。

  • ファイル名が style.css になっているか(styles.css ではない)
  • 1行目が /* で始まっているか。その前に空行やBOMが入っていないか
  • Theme Name:Template: の両方があるか
  • コメントが */ で閉じられているか

エディタの文字コードは UTF-8(BOMなし) にしてください。Windowsのメモ帳で保存するとBOMが付くことがあります。

「壊れています」「親テーマが見つかりません」と出る

Template: の値が親テーマのフォルダ名と一致していません。表示名ではなくフォルダ名です。wp-content/themes/ を開いて、実際のディレクトリ名をそのままコピーしてください。大文字小文字も区別されます。

有効化したらレイアウトが崩れた

親テーマのCSSが読めていません。ブラウザの検証ツールで「ネットワーク」タブを開き、親テーマのCSSファイルがリクエストされているか見てください。

出ていない場合は、$parent_handle の値が間違っています。前述の「ハンドル名の調べ方」で、HTMLソースの id 属性から正確な値を取り直してください。

親テーマを一度有効化してからソースを見ると、確実にハンドル名が分かります。

子テーマのCSSが親に負ける

読み込み順は合っているのに効かない場合、セレクタの詳細度が足りていません。検証ツールで対象の要素を選び、Stylesパネルで自分の書いたルールに打ち消し線が入っていないか確認します。打ち消されているなら、そのすぐ上に表示されているルールが勝っています。同じセレクタをコピーして子テーマ側に書けば同点になり、後から読まれる子テーマが勝ちます。

画面が真っ白になった(Fatal error)

functions.php の書き間違いです。よくあるものを3つ。

症状 原因
画面全体が真っ白 PHPの構文エラー(セミコロン抜け、括弧の閉じ忘れ)
Cannot redeclare function 親テーマと同じ関数名を定義した。接頭辞を付ける
Headers already sent <?php の前に空白・改行・BOMがある

FTPで functions.php の中身を空(<?php の1行だけ)に戻せば復旧します。管理画面に入れなくなっても、ファイルを直接触れば戻せるので慌てないでください。

なお、wp-config.php に次の2行を入れておくと、白い画面ではなくエラー内容が読めます。開発環境のみで使ってください。

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_DISPLAY', true );

本番で有効にすると、パスや内部情報が訪問者に見えてしまいます。作業が終わったら必ず false に戻します。本番でどうしても内容を見たいときは、WP_DEBUG_DISPLAYfalse のままにして WP_DEBUG_LOGtrue にし、wp-content/debug.log に出す形にしてください。画面には出ません。

テンプレートを置いたのに反映されない

3つ確認します。

  • ファイルパスが親テーマと同じ相対パスになっているか(template-parts/ などのフォルダも再現する)
  • 親テーマがそのファイルを get_template_part() で読んでいるか(require なら上書きできない)
  • キャッシュ系プラグインやサーバー側のキャッシュが残っていないか

キャッシュは意外と多い原因です。判断に迷ったら、シークレットウィンドウで開き直してみてください。

親テーマの更新後に表示が変わった

親テーマ側でハンドル名やテンプレート構造が変わった可能性があります。上書きしているテンプレートは、親の更新時に差分を確認する必要があります。子テーマにコピーしたファイルは親の更新から取り残されるため、機能追加や不具合修正が反映されません。

上書きするテンプレートは最小限にとどめ、フィルターフックで済ませられるところはフックで書くほうが、長期的な保守コストは下がります。子テーマにコピーしたファイルの一覧を、子テーマ内の README.md に書き残しておくと、更新時に見るべき箇所がすぐ分かります。

更新前後の状態を戻せる体制については、こちらでツール構成をまとめています。


子テーマを作るべきか判断する基準

子テーマを作るべきか判断する基準

最後に、そもそも子テーマが必要かの話をします。全案件で作るものではありません。

状況 判断
有料テーマ/配布テーマを使い、CSSやPHPを触る 子テーマを作る
CSSの調整が数行だけ 「追加CSS」(カスタマイザー)で十分
自作テーマ(更新の概念がない) 子テーマは不要。本体を直接編集する
ブロックテーマでスタイルだけ変える サイトエディターの「スタイル」で完結する場合が多い
プラグインで機能を足す 子テーマではなくプラグイン側の設定を先に確認

「追加CSS」は WordPress 4.7 以降のカスタマイザーにある機能で、DBに保存されます。テーマを切り替えると引き継がれない点だけ注意してください。数行なら十分ですが、行数が増えると差分管理もコードレビューもできなくなります。バージョン管理下に置きたくなった時点で子テーマに移すのが現実的な線引きです。

PHPを1行でも書くなら子テーマを作ってください。functions.php を親テーマで直接編集すると、テーマ更新で消えます。「更新しなければいい」という判断もありますが、セキュリティ修正を含む更新を止めることになるので、長期運用のサイトでは選びづらい選択です。

クライアントに納品する場合に足しておくこと

制作案件として子テーマを渡すなら、次の3つを添えておくと引き継ぎで揉めません。

  • 上書きしたテンプレートの一覧 — 親テーマ更新時に確認すべきファイルが分かる
  • $parent_handle に何を入れたか — 親テーマのバージョンアップでハンドル名が変わったときの調査起点になる
  • 子テーマのバージョン運用ルールVersion: を手で上げるのか filemtime() に任せているのか

保守を別の会社が引き継ぐ可能性を前提に書いておくのが安全です。作った本人しか読めないコメントを残すより、判断の根拠を1行書いておくほうが後の工数を減らします。

サーバー環境やCMSの選定から見直したい場合は、こちらも参考になります。


よくある質問

よくある質問

子テーマを作るとサイトの表示速度は遅くなりますか

CSSファイルが1本増えるため、リクエストは1つ増えます。ただし体感できる差にはならないのが実情です。それより、子テーマで不要なスクリプトの読み込みを止めるほうが速度への影響は大きくなります。速度を理由に子テーマを避ける判断は、得られるものと失うものが釣り合いません。

子テーマを有効化するとウィジェットやメニューの設定は消えますか

テーマを切り替えると、ウィジェットの配置とメニューの割り当てはリセットされることがあります。子テーマは親とは別のテーマとして扱われるためです。カスタマイザーの設定も引き継がれません。切り替える前に現在の設定をスクリーンショットで残しておくと、復元が早く済みます。投稿・固定ページ・画像などのコンテンツは影響を受けません。

親テーマが更新されたら子テーマも作り直す必要がありますか

基本的には不要です。style.css と functions.php だけの子テーマなら、親の更新でそのまま動き続けます。作り直しが必要になるのは、親テーマのテンプレートをコピーして上書きしている場合です。親側でそのファイルが変更されると、子テーマの古い内容が使われ続けます。上書きしたファイルの一覧をメモしておき、親の更新時に差分を確認してください。

子テーマの子テーマ(孫テーマ)は作れますか

WordPress標準では作れません。Template: に指定できるのは親テーマ1階層までで、子テーマを Template: に指定しても正しく動作しません。孫テーマが欲しくなる場面では、機能をプラグイン化して分離するか、mu-plugins に置く方法を検討してください。

functions.php に書いたコードはプラグインに移すべきですか

テーマの見た目に関わる処理はfunctions.php、サイトの機能に関わる処理はプラグインが原則です。ショートコードやカスタム投稿タイプをfunctions.phpに書くと、テーマを変えたときに投稿が表示されなくなります。テーマを変えても残したい機能は、プラグイン(または wp-content/mu-plugins/)に置いてください。

既に親テーマを直接編集してしまった場合はどうすればいいですか

先に、編集した箇所を特定します。親テーマの配布元から同じバージョンをダウンロードし、手元のファイルと差分を取れば、追加・変更した行が分かります。その差分を子テーマ側へ移してから、親テーマをクリーンな状態に戻して更新してください。差分が取れない状態なら、親テーマのフォルダを丸ごとバックアップしてから更新し、崩れた箇所を子テーマ側で作り直すほうが早い場合もあります。


まとめ

WordPress 子テーマの作り方で押さえるべき点を整理します。

  • 最小構成は style.css と functions.php の2つTemplate: に親テーマのフォルダ名を正確に書く
  • 親CSSの読み込みは、親のハンドルに依存させる@importget_template_directory_uri() 直指定は避ける
  • バージョン指定を必ず入れるwp_get_theme()->get('Version')filemtime() でキャッシュを切る
  • テンプレートの上書きは同じ相対パスに同名で置くrequire で読まれているファイルは上書きできない
  • functions.php だけは上書きではなく追加。関数名には接頭辞を付ける
  • 親の処理を消すときは remove_action()。フック名・関数名・優先度を完全一致させる

「動くけど正しくない」書き方でも表示はできてしまうため、問題が出るのは決まって数か月後、親テーマが更新されたタイミングです。最初に正しい形で作っておけば、その時点で慌てる必要がなくなります。

制作者向けの実装記事は今後も継続して出していきます。手元の案件で詰まった箇所があれば、その解決手順もいずれ記事として扱う予定です。