今回はGruntを使い始めた頃から始めた、テーマ作成の方法とそのファイル構成について触れます。ビルドシステムを使ううえで便宜上こうなった、という類いの内容なので、参考程度にご覧ください。投稿作成にあたり、気付いて改良した内容も含みます。
ビルドシステムとその他ツール
プロジェクトディレクトリの構成
WordPress本体以外をgit管理するために、下記のような構成にしています。Wordpress本体はルートディレクトリの変更をする前提です。
project-name/
├ _src/ // 作業ディレクトリ
│ ├ _bower_components/ // bowerでインストールするライブラリの管理用
│ ├ ai // Illustratorファイル管理用
│ ├ doc // 資料管理用
│ ├ fonts/ // Webフォント管理用
│ ├ images/ // 画像管理用
│ ├ jade/ // 静的サイトファイル作成用(Wordpressの場合は使わない)
│ ├ js/ // javascript管理用
│ ├ project.json // jade用
│ ├ psd/ // Photoshopファイル管理用
│ ├ sass/ // sass管理用
│ └ wpjade/ // WordPressテンプレファイル作成用
├ .bowerrc // bowerでインストールするライブラリの管理ディレクトリ指定用
├ .editorconfig // エディタ設定用
├ .htaccess // WordPress用 htaccess
├ bower.json // bowerでインストールするライブラリ管理用
├ gulpfile.js // Gulpファイル
├ index.php // WordPress用 index.php
├ package.json // node_modules 管理用
├ settings.json // Gulp設定用
└ wp/ // WordPress本体
wpjadeディレクトリ内
wpjade内のjadeファイルをphpファイルとしてコンパイルする前提で下記のような構成にしています。
wpjade/
├ assets/
│ ├ functions.php
│ ├ screenshot.png
│ └ style.css
├ index.jade
├ single.jade
├ page.jade
├ 404.jade
├ その他、テンプレファイル群
└ templates/
├ _fb_sdk.jade
├ _ga.jade
├ _layout.jade
├ _ogp.jade
├ _site_tree.jade
└ php/
├ _mixin.jade
└ phpを記述したファイル群
テーマ用ファイルの内容
Theme Checkで得た情報を元に編集した、下記ファイルについてです。
- function.php
- style.css
- screenshot.png
assets内ファイルの記述
functions.php(関連する内容のみ)
テーマディレクトリ内のcss/、js/内のファイルなどを読ませるための記述を入れてます。あと、タイトルタグ出力用の設定などを。下記サイトを参照させていただきました。
- [WordPress] WordPress 4.6 からヘッダーに表示されるようになった DNS プリフェッチを非表示にする | memocarilog
- WordPressでJavascriptの読み込み位置を調整する | work.log
- wp_localize_script() テーマファイルのURLなどをjsファイルに渡す – Qiita
- <script>タグに defer/async を付与する方法 – 零弐壱蜂
- Nu Html Checkerが「type属性省略すべし」と警告を出すので非表示運用にした【WordPress】
- WordPress:add_theme_support( ‘title-tag’ ); 使用時にタイトルからキャッチフレーズを削除したりセパレータを変更する方法 | NxWorld
- WordPressで「スクロールせずに見えるコンテンツのレンダリングをブロックしているJavaScript/CSSを排除する」に対応する : トイレのうず/ぼやき
// ----------------------------------------- // wp-head 削除項目 // ----------------------------------------- remove_action('wp_head', 'feed_links', 2); remove_action('wp_head', 'feed_links_extra', 3); remove_action('wp_head', 'rsd_link'); remove_action('wp_head', 'wlwmanifest_link'); remove_action('wp_head', 'index_rel_link'); remove_action('wp_head', 'parent_post_rel_link', 10, 0); remove_action('wp_head', 'start_post_rel_link', 10, 0); remove_action('wp_head', 'adjacent_posts_rel_link_wp_head', 10, 0 );
remove_action('wp_head', 'locale_stylesheet');remove_action('wp_head', 'wp_print_styles', 8);remove_action('wp_head', 'wp_generator'); remove_action('wp_head', 'wp_shortlink_wp_head'); remove_action('wp_head', 'rel_canonical'); // 絵文字関係削除 remove_action('wp_head', 'print_emoji_detection_script', 7); remove_action('admin_print_scripts', 'print_emoji_detection_script'); remove_action('wp_print_styles', 'print_emoji_styles' ); remove_action('admin_print_styles', 'print_emoji_styles'); // Embed関係削除 remove_action('wp_head','rest_output_link_wp_head'); remove_action('wp_head','wp_oembed_add_discovery_links'); remove_action('wp_head','wp_oembed_add_host_js'); // headへの//s.w.orgのDNDプリフェッチ挿入停止 function remove_dns_prefetch( $hints, $relation_type ) { if ( 'dns-prefetch' === $relation_type ) { return array_diff( wp_dependencies_unique_hosts(), $hints ); } return $hints; } add_filter( 'wp_resource_hints', 'remove_dns_prefetch', 10, 2 ); // ----------------------------------------- // 読み込みファイルの設定 // ----------------------------------------- if (!is_admin()) { // 登録の解除 function deregister_script(){ wp_deregister_script('jquery'); } // 登録する項目 function register_script(){ // ファーストビュー用CSS wp_register_style('fv', false); // nullでバージョン除去、最後の引数がtrueだとフッターに出力されるwp_register_script( 'html5shiv', get_stylesheet_directory_uri() . '/js/html5shiv.min.js', '', null, false);wp_register_script( 'css3mediaqueries', get_stylesheet_directory_uri() . '/js/css3-mediaqueries.js', '', null, false);wp_register_script( 'jquery-original', get_stylesheet_directory_uri() . '/js/jquery.min.js', '', null, true); wp_register_script( 'js_project', get_stylesheet_directory_uri() . '/js/project.min.js', array( 'jquery-original' ), null, true); } // 登録した外部ファイルを出力 function add_script() { deregister_script(); register_script(); wp_enqueue_style('fv');wp_enqueue_script('html5shiv');wp_enqueue_script('css3mediaqueries');wp_enqueue_script('jquery-original'); wp_enqueue_script('js_project');// IEの条件付きコメントwp_script_add_data('html5shiv', 'conditional', 'lt IE 9');wp_script_add_data('css3mediaqueries', 'conditional', 'lt IE 9');} add_action('wp_enqueue_scripts', 'add_script'); // 外部JS(project.min.js)にWPのテンプレートタグを渡す function wp_to_js() { $tRoot = esc_url(get_template_directory_uri()); wp_localize_script( 'js_project', 'theme', array('url' => $tRoot)); } add_action('wp_enqueue_scripts', 'wp_to_js');// スクリプトにdefer追加 function defer_enqueue_script( $tag ) { return str_replace( ' src', ' defer src', $tag ); } add_filter( 'script_loader_tag', 'defer_enqueue_script', 10, 2 );// インラインCSSの追加 function add_inline_css() { $inline_css = file_get_contents( get_stylesheet_directory_uri() . '/css/fv.css', true); wp_add_inline_style( 'fv', $inline_css ); } add_action( 'wp_enqueue_scripts', 'add_inline_css' ); // jsとcssのtype属性を除去 function remove_type_attr($tag) { return preg_replace("/type=['\"]text\/(javascript|css)['\"]/", '', $tag); } add_filter('script_loader_tag', 'remove_type_attr'); add_filter('style_loader_tag', 'remove_type_attr'); } // ----------------------------------------- // タイトルタグの出力 // ----------------------------------------- // add_theme_support( 'title-tag' ); を使用する function setup_theme() { add_theme_support( 'title-tag' ); } add_action( 'after_setup_theme', 'setup_theme' ); // タイトルからキャッチフレーズを削除する function remove_tagline($title) { if ( isset($title['tagline']) ) { unset( $title['tagline'] ); } return $title; } add_filter( 'document_title_parts', 'remove_tagline' ); // セパレータを任意のものに変更する function custom_title_separator($sep) { $sep = '|'; return $sep; } add_filter( 'document_title_separator', 'custom_title_separator' );
CSSの非同期読み込み
レンダリングブロック回避用にloadCSS.jsとonloadCSS.jsで外部CSSの読み込みを制御します。プロジェクト用CSS(project.css)は、ファーストビュー用と切り分けたのちに利用します。下記の順番で処理する場合の記述を記載しておきます。
PageSpeed InsightsだとCSSが二重に認識されたりしてレンダリングブロック扱いになるんですが、体感速度は上がります。
例
- ファーストビュー用CSSのインライン挿入(function.phpで処理済み)
- プロジェクトテーマ用CSS読み込み
- normalize.css > テーマ用CSS読み込み後の処理
- Google Fontsの読み込み > テーマ用CSS読み込み後の処理
project.min.js
$(function(){
// インラインCSS(ファーストビュー用CSS)の上にCSSを順番に挿入 / xxxxxxxはタイムスタンプ
var theme_css = loadCSS( theme.url + '/css/project.css?xxxxxxx', document.getElementById("fv-inline-css") );
onloadCSS( theme_css, function() {
(読み込み後の処理)
var
normalize = theme.url + '/css/normalize.css',
gf = '//fonts.googleapis.com/css?family=Roboto';
loadCSS( normalize, document.getElementById("fv-inline-css") );
loadCSS( gf, document.getElementById("fv-inline-css") );
}
});
Javascriptの非同期読み込み
(現在、保留中です。)
style.css
Tagsは入れても入れなくてもTheme Check で怒られるので入れていません。テキストドメインのエラーが未だわからず、という状況です。
下記の記事のおかげで、タグの指定内容が決まっていることがわかりました。Theme Tagsの内容を参照して指定しておけば良いようです。
WordPressテーマを自作する際のstyle.cssに記述するテーマ情報のテンプレート(子テーマ制作にも対応) | オレインデザイン
/*
Theme Name: (テーマ名)
Theme URI: (配布テーマではないので使用するWebサイトのドメインを入れてます。)
Description: (テーマの簡単な説明)
Version: 1.0
Author: Shinichi Kuroda
Author URI: http://www.studiobusstop.com/
License: GNU General Public License v2 or later // WordPress公式に準じて(詳しくないです)。
License URI: http://www.gnu.org/licenses/gpl-2.0.html // WordPress公式に準じて。
Text Domain: (テーマディレのスラッグ名にとりあえず設定しています。)
Tags: (Theme Tags の内容を参照してコンマ区切りで指定)
*/
screenshot.png
WordPress推奨の880x660pxで作成します。すると、Theme Checkで怒られるので、1200x900pxで作成します。「screenshot」なので、本来はテーマのスクリーンショットを入れるべきなのですが、制作者クレジット代わりにロゴを入れることが多いです。
テンプレート
WordPressのテンプレートパーツ機能は使わず、jade(pug)の extends と include を使ってテンプレートを作成する前提として、ベースとなる_layout.jadeを作成します。
テンプレートに記述するWordpress独自関数
Theme Check の情報を元に下記の関数を設定します。
基本
アクションフック用
クラス付加用
js読み込み用
ファビコン
ファビコン指定はサイトアイコン機能に任せる前提です。
wpjade/templates/_layout.jade
block current
block single
block pghdr
block row
-var root = '<?php echo esc_url(home_url()); ?>'
-var tRoot = '<?php echo esc_url(get_template_directory_uri()); ?>'
doctype
html()
head(prefix=( current === 'home' ? 'og: http://ogp.me/ns# website: http://ogp.me/ns/website#' : 'og: http://ogp.me/ns# article: http://ogp.me/ns/article#' ) )
meta(charset!='<?php bloginfo("charset"); ?>')
include _site_tree
| <?php wp_head(); ?>
include _ogp
include ./php/canonical.php
//- css
link(rel='stylesheet', media='screen', href!='#{tRoot}/css/#{name}.css')
//- コメント返信のスクリプト(停止)
//- | <?php if ( is_singular() ) wp_enqueue_script( 'comment-reply' ); ?>
body(<?php body_class(); ?>)
#container
header.m-hdr
h1.logo
// ... ヘッダー用の記述
main.m-body
-if( pghdr )
header.m-pg-hdr(class='#{current}')
block pghdrContent
-if( row )
.m-cont.l-flex
article(<?php post_class('l-main', 'l-float-l'); ?>)
block content
.l-sub.l-float-r
block subcontent
-else
article(<?php post_class('m-cont'); ?>)
block content
footer.m-ftr
// ... フッター用の記述
| <?php wp_footer(); ?>
block script
wpjade/templates/_ogp.jade
meta(property='og:title' content!='')
-if( current === 'home' )
meta(property='og:type' content='website')
meta(property='og:description' content='#{des}')
meta(property='og:url' content!='#{root}/')
-else
meta(property='og:type' content='article')
| <?php $current_url = esc_url( home_url() . $_SERVER['REQUEST_URI'] ); ?>
meta(property='og:url' content!='')
meta(property='og:image' content!='#{tRoot}/images/ogimg.png')
meta(property='og:site_name' content!="")
wpjade/templates/php/canonical.php(カノニカルの設定)
ページネーションのSEO ( rel=”canonical” / “next” / “prev” )に記載。
テンプレート本体の構成
extends ./templates/_layout
include ./templates/php/_mixin
block current
-var current = 'news'
block pghdr
-var pghdr = 1
block row
-var row = 1
block single
-var single = 1
block pghdrContent
// 必要な記述
block content
// 必要な記述
block subcontent
// 必要な記述
block script
script.
window.addEventListener( 'load', function(){
$(function(){
// 必要な記述
});
});
最終的なテーマディレクトリ内
wp/
└ wp-content/
└ themes/
└ project-name/
├ css/
│ ├ fv.css
│ ├ normalize.css
│ └ project.css
│
├ fonts/
├ images/
├ js/
│ ├ jquery.min.js
│ ├ jquery.min.map
│ └ project.min.js / loadCSS、onloadCSSを含む
│
├ languages/
├ functions.php
├ screenshot.png
├ style.css
├ index.php
├ single.php
├ page.php
├ 404.php
└ その他、テンプレファイル群
さいごに
bowerは主にjQueryとnormalize-css用で、いいかげんnpmで一元管理したいと思いつつ、そのままな状況です。
以上です。