FlatIsleロゴ

Flat Isle 日誌

WordPressテーマの作成(ブロック編)

2022-08-09

前回はWordPressテーマを作成する上での、登録方法及び基本ファイルについて説明しました。
今回は、記事の作成を効率的に行うためのオリジナルブロック及び、ショートコード等を用いたブロックの作成方法を解説します。また、CSSについても説明します。

オリジナルブロックの作成・登録

オリジナルデザインのブロックを使用するには、クラスセレクタで指定したデザインをstyle.cssに作成し、ブロックに「追加CSSクラス」を指定する必要があります。
ですが「追加CSSクラス」の指定は「設定」サイドバーを開く必要があるため、忘れがちになります。
そこでオリジナルブロックを作成・登録して作業漏れを防ぎましょう。
オリジナルブロックはJavaScriptとCSSの2種類のファイルで作成できます。(JSONファイルを使う方法もありますが、ここでは古い方法で説明しています。)
以下に『注意』のタイトル付き囲み枠ブロックの作成方法を例として説明していきます。

ブロックの定義(JavaScript)

JavaScriptファイル「block.js」の内容は下記の通りです。各部の説明は下表を参照して下さい。

(
function(blocks, element, blockEditor){
	var el               = element.createElement;
	var InnerBlocks      = blockEditor.InnerBlocks;
	var useBlockProps    = blockEditor.useBlockProps;

	blocks.registerBlockType('fla-block/caution', {
		title:    'Caution',
		icon:     'tag',
		category: 'text',
		example: {
			attributes:{
				content: "注意"
			}
		},
		edit: function(props){
			return el(
				'div',
				useBlockProps({className: props.className}),
				el(InnerBlocks)
			);
		},
		save: function(){
			return el(
				'div',
				useBlockProps.save(),
				el(InnerBlocks.Content)
			);
		},
	});
}(window.wp.blocks, window.wp.element, window.wp.blockEditor)
);
内容
2~5,31ブロックで使用する機能の指定(詳細は後述)
7ブロックの登録(ブロック名の命名規則は後述)
9編集時に表示されるアイコンの指定(公式の一覧
10ブロックの分類(編集時にどこに表示されるか。公式の解説
16~22編集時の描画内容(今回は入れ子構造に対応)
23~29保存(公開時)の描画内容

ブロックで使用する機能の指定

オリジナルブロックを登録する際、ブロックで使用する基本機能(関数等を含んだオブジェクト)を引数に指定します。(自動生成のツールもありますが、手動でも作れそうです)
以下に公式のサンプルより抽出した一覧を記載します。(他にもあるかもしれません)

  • blocks(window.wp.bloks)
     ブロックの機能を使用するため必須
  • element(window.wp.element)
     ブロック内で使用するタグ(エレメント)を使用するため必須
  • editor(window.wp.editor),
  • blockEditor(window.wp.blockEditor)
     ブロック内で入力用パネル等を表示する
  • components(window.wp.components)
     ボタンやセレクトボックス等のUIを使用する場合必須
  • i18n(window.wp.i18n)
     多言語対応に使用する

命名規則

「registerBlockType」の第1引数に指定するブロック名は、「namespace/block-name」で一意となる様に指定します。
この名前はコメントに<!– wp:namespace/block-name –>で表示される他、タグのクラス名にも「wp-block-namespace-block-name」が追加されます。

公式の解説ページです。
Registration (Block Name)

ブロックの設定

「registerBlockType」の第2引数でブロックの設定を記述します。記述方法は、設定項目と値の組み合わせを「{key:value, key:value, …}」のように指定します。

公式の解説ページです。
Registration (Block Configration)

CSSの作成

CSSはオリジナルブロック用に別ファイルで読み込む事も出来ますが、数百バイト程度ならファイルを分けない方が良いと考え、「style.css」に統合しました。
今回のタイトル付き囲み枠ブロック用に記述した内容は下記の通りです。

/* 追加ブロック //////////////////////////////*/
.wp-block-fla-block-caution {
	position:			relative;
	padding:			1.1rem 0.5rem 0.5rem 1rem;
	margin:				1.3rem 1rem 1rem 1rem;
	font:				0.9em "Hiragino Kaku Gothic ProN", "Hiragino Sans", Meiryo, sans-serif;
	color:				#000;
	background-color:	var(--wp--preset--color--background);
	border:				3px solid #955;
	border-radius:		8px;
}
.wp-block-fla-block-caution:before {
	position:			absolute;
	top:				-1.2rem;
	left:				0.8rem;
	padding:			0 0.2rem;
	background-color:	inherit;
	font:				1.5rem "Noto Color Emoji", "Apple Color Emoji", "Segoe UI Emoji", Times, Symbola, Aegyptus, Code2000, Code2001, Code2002, Musica, serif, LastResort;;
	content:			"🚫";
}
.wp-block-fla-block-caution:after {
	position:			absolute;
	top:				-0.8rem;
	left:				3.2rem;
	padding:			0 0.2rem;
	background-color:	inherit;
	font:				bold 1.2em "Hiragino Kaku Gothic ProN", "Hiragino Sans", Meiryo, sans-serif;
	color:				var(--wp--preset--color--block-caution);
	content:			"注意";
}

上記例のタイトル付き囲み枠ブロックは、アイコンをbefore要素で、タイトルをafter要素で表示しています。
(アイコン及びタイトルの文字色・文字サイズを個別に指定するため、before要素とafter要素に分けています)

ブロックの登録

ブロックの登録は「functions.php」にて行いますが、毎回本ファイルに追加すると煩雑になります。
今回はblocksフォルダに登録用PHPファイルを作成し、それをfunctions.php内でインクルードして使います。

require get_template_directory()."/blocks/register_block.php";

実際の登録処理は以下の様になります。(register_block.phpに記述)

function block_caution_enqueue(){
	wp_register_script(
		'fla-block-caution-script',
		get_theme_file_uri('/blocks/caution/block.js'),
		array('wp-blocks', 'wp-element', 'wp-editor', 'wp-block-editor'),
		filemtime(get_theme_file_path('/blocks/caution/block.js'))
	);
	register_block_type(
		'fla-block/caution',
		array(
			'editor_script' => 'fla-block-caution-script'
		)
	);
}
add_action('init','block_caution_enqueue');

記事目次の作成・表示

長い記事を読む時に目次があると便利ですが、これを手作業で作成するのは大変です。自動生成出来る様にPHPファイルを作成します。

目次の作成

記事の表示時にH2~H5のタグを正規表現にて抽出し、各タグの「id」に目次用IDを付与します。(例:idx-1-2-1)
また、PHPグローバル変数に目次用リストタグを生成し、アクションフックで呼び出せる関数にて本値を返します。

// 目次の生成・表示 ////////////////////////////////
if(!function_exists('create_heading_index')){
	function create_heading_index($content){
		if(is_single() && in_the_loop() && is_main_query()){
			global $gHeadingIndex;			// グローバル変数として保存
			$wkIdx  = array(0, 0, 0, 0);	// タグ階層管理用
			$idxPos = 0;					// 現在の階層

			$gHeadingIndex = "<nav class='heading-index'><ul>";

			preg_match_all('#<h[2-5][^>]*>(.*?)</h[2-5]>#i', $content, $matches, PREG_SET_ORDER);
			foreach($matches as $element){
				$wkIdxPos = (int)substr($element[0], 2, 1) -2;								// Hタグの値を取得(0基準にするため-2)
				$wkIdx[$wkIdxPos] = ($idxPos < $wkIdxPos) ? 1 : ($wkIdx[$wkIdxPos] + 1);	// 階層が下がった場合、1から始める
				$idxPos = $wkIdxPos;														// 現在の階層を更新
				$indexId = "idx-".(string)$wkIdx[0]
				.(($wkIdxPos > 0) ? '-'.(string)$wkIdx[1] : "")
				.(($wkIdxPos > 1) ? '-'.(string)$wkIdx[2] : "")
				.(($wkIdxPos > 2) ? '-'.(string)$wkIdx[3] : "");							// id文字列を生成
				// 目次用文字列作成
				$gHeadingIndex .= "<li id='h".$indexId."'>".str_repeat("&nbsp;&nbsp;", $idxPos)."<a href='#".$indexId."'>".$element[1]."</a></li>";
				// 本文のタグ追加
				$wkstr = substr($element[0], 0, 3)." id='".$indexId."'".substr($element[0], -1* (strlen($element[0])-3));
				$content = preg_replace("#".preg_quote($element[0])."#", $wkstr, $content, 1);
			}
			$gHeadingIndex .= "</ul></nav>";
		}
		return $content;
	}
}
add_filter('the_content', 'create_heading_index');

if(!function_exists('hook_fnc_heading_index')){
	function hook_fnc_heading_index(){
		global $gHeadingIndex;
		if($gHeadingIndex) echo $gHeadingIndex;
	}
}
add_action('hook_heading_index', 'hook_fnc_heading_index', 11);

functions.phpにて上記PHPファイルをインクルードします。

require get_template_directory()."/blocks/heading_index.php";

目次の表示

本目次を表示するには以下の様にアクションフックを呼び出します。(sidebar.phpの例。記事ページにのみ表示するため is_single関数で判定しています)

if(is_single()){
	do_action('hook_heading_index');
}

また、スクロール時に目次のハイライト表示とページの表示領域を同期させるため、以下のJavaScriptを記述します。

<script>
	// 目次と表示領域の同期表示
	window.addEventListener('DOMContentLoaded', function(){
		const aryContents = document.querySelectorAll('article h2, article h3, article h4, article h5');
		const aryPosY     = new Array();
		let   prePos      = 0;
		let   winHOffset  = window.innerHeight / 4;
		// コンテンツ内の見出しの位置を配列に保存(高速化のため)
		aryContents.forEach(function(content, i){
			aryPosY.push(content.getBoundingClientRect().top + window.pageYOffset); // Not (content.offsetTop);
		});
		// スクロール判定
		window.addEventListener('scroll', function(e){
			for(i = aryPosY.length; i >= 0 ; i--){
				if(aryPosY[i] < window.scrollY + winHOffset){ //画面1/4の時点で見出しを超えたか判定
					if(prePos != i){ //高速化のため、必要な場合のみ更新処理を行う
						document.getElementById('h' + aryContents[prePos].id).classList.remove('active');
						document.getElementById('h' + aryContents[i     ].id).classList.add('active');
						prePos = i;
					}
					break;
				}
			}
		});
	});
</script>

目次の装飾

style.cssに以下の記述をし、目次の外観を整えます。

/* 目次 */
body aside nav {
	display:			block;
	position:			relative;
	left:				0px;
	width:				100%;
	margin:				10px 0;
	padding:			1.75rem 5px 5px 5px;
	border:				solid 1px var(--wp--preset--color--border);
	background-color:	var(--wp--preset--color--background);
}
.singular aside nav.heading-index::before {
	display:			block;
	position:			absolute;
	top:				0;
	left:				0;
	width:				100%;
	color:				var(--wp--preset--color--title-font);
	background:			var(--wp--preset--color--title-bg);
	text-align:			center;
	content:			"目次";
}
.singular aside nav.heading-index li.active {
	background-color:	var(--wp--preset--color--index-bg);
}

パンくずリストの作成・表示

WordPressにはパンくずリストのブロックがありません。
サイト構造を明確にするために、パンくずリストを作成して表示させます。

パンくずリストの作成

パンくずリストは表示している画面の種類により表示内容が変わってきます。if文により条件分岐し、最後に表示する内容を切り替えていきます。

if(!function_exists('fnc_breadcrumb')){
	function fnc_breadcrumb() {
		global $post;
		global $cat;
		$html = "<ul><li><a href='".get_bloginfo('url')."'>🏠HOME</a></li>";
		if(is_category()){
			global $cat;
			
			$aryParents = get_ancestors($cat, 'category');
			foreach($aryParents as $parent){
				$html .= "<li><a href='".get_category_link($parent)."'>📂".get_the_category_by_ID($parent)."</a></li>";
			}
			$html .= "<li><a href='".get_category_link($cat)."'>📂".get_the_category_by_ID($cat)."</a></li>";
		} else
		if(is_single()){
			$cat_id     = get_the_category()[0]->cat_ID;
			$aryParents = get_ancestors($cat_id, 'category');
			foreach($aryParents as $parent){
				$html .= "<li><a href='".get_category_link($parent)."'>📂".get_the_category_by_ID($parent)."</a></li>";
			}
			$html .= "<li><a href='".get_category_link($cat_id)."'>📂".get_the_category_by_ID($cat_id)."</a></li>";
			$html .= "<li>📰".get_the_title()."</li>";
		} else
		if(is_archive()){
			$html .= "<li>📚".get_the_archive_title()."</li>";
		} else
		if(is_page()){
			$html .= "<li>📰".get_the_title()."</li>";
		} else
		if(is_search()){
			$html .= "<li>🔎検索結果:(".get_search_query().")</li>";
		} else
		if(is_404()){
			$html .= "<li>👻404</li>";
		}
		$html .= "</ul>";
		return $html;
	}
}
add_shortcode('breadcrumb', 'fnc_breadcrumb');

functions.phpにて上記PHPファイルをインクルードします。

require get_template_directory()."/blocks/breadcrumb.php";

下記のページが参考になります。
WordPress|パンくずリストを自作する方法

パンくずリストの表示(ショートコード)

記事ページ内でパンくずリスト作成関数を呼び出すため、breadcrumb.phpの末尾でショートコードとして登録しています。
add_shortcode関数のパラメータは、ショートコード名、呼び出す関数名です。

add_shortcode('breadcrumb', 'fnc_breadcrumb');

登録したショートコードを使うには下記の様に記述します。(singular.phpの例)

<?php echo do_shortcode('[breadcrumb]'); ?>

パンくずリストの装飾

本パンくずリストはクリック可能な要素のため、style.cssに以下の内容を指定します。

/* パンくずリスト ////////////////////////*/
div.breadcrumb {
	margin:				10px 10px 10px 0;
}
div.breadcrumb ul {
	padding:			0 4px;
	list-style:			none;
	background-color:	var(--wp--preset--color--transparent-bread);
}
div.breadcrumb ul li {
	display:			inline-block;
	margin:				4px 0;
	font-size:			0.9rem;
}
div.breadcrumb ul li + li::before {
	content:			"\00a0>\0020";
}
div.breadcrumb ul li a {
	color:				var(--wp--preset--color--font);
	text-decoration:	none;
}

再利用ブロックの使用

ショートコードや複数ブロックの組み合わせを「再利用ブロック」として登録しておくと、記事作成が効率化できます。
例えばAdsense広告を記事ページの任意の場所に表示したい場合、次のように登録します。

  1. 広告の表示スクリプトをショートコードとして作成・登録します
  2. 記事内に「ショートコード」ブロックを挿入します
  3. 挿入したブロックに1.のショートコード名を記述します
  4. 上記のブロックを「再利用ブロック」として登録します(例えば名前を「ads」として作成)

登録した再利用ブロックを呼び出すには、空のブロック選択時に「/登録した名前」(上記の例では「/ads」)と入力します。
再利用ブロックの利点は下記の通りです。

  • 複数の作業ステップが1回のステップで完了する(上記例なら2.と3.のブロック挿入が「/ads」のみで完了する)
  • 再利用ブロックのうち1つを修正すると、同名の再利用ブロックも自動的に修正される

公式の解説ページが参考になります。
「再利用ブロック」を活用してみよう

CSSの記述

CSSに関して、便利な記述や注意が必要な点について下記にまとめました。

変数の利用

テーマ全体で共通して使用する物を変数に格納しておくと、変更時の修正箇所を減らせるため保守性が向上します。
下記の例では背景や線の色を変数化しています。使う色を限定すればデザインの統一感も出せるので一石二鳥です。

body {
	--wp--preset--color--background:		#ffffff;
	--wp--preset--color--font:				#000000;
	--wp--preset--color--title-font:		#ffffff;
	--wp--preset--color--title-bg:			linear-gradient(to right,  #0ba6ff  0%, #3333ff  25%, #3333ff  75%, #0ba6ff 100%);
	--wp--preset--color--border:			#888888;
	--wp--preset--color--transparent-aside:	rgba(255, 255, 255, 0.8);
}

使用する時は var()関数に変数名を書いて呼び出します。

background-color:	var(--wp--preset--color--background);

モバイルフレンドリー対応

別の記事でも説明していますが、Googleのモバイルフレンドリーテストをクリアするため、クリック可能な要素のmarginプロパティを8px以上に指定しておきます。
パンくずリスト等、クリック可能な要素を独自に増やしている場合は、必ずmarginプロパティに8px以上を指定しているか確認しておきましょう。

/* モバイルフレンドリー対応 //////////////////*/
a {
	display:			inline-block;
	margin:				8px;
	line-height:		1rem;
}
input, button, select, textarea {
	margin:				8px;
}

エディター用CSS

WordPressは編集画面用のCSSを別途指定できます。
before要素、after要素を多用している場合、編集画面で正しく表示出来ない事があります。
そのため、style.cssの一部を書き換えた物をエディター用CSSとして使用します。
下記はeditor.cssで、前述のタイトル付き囲み枠ブロック用CSSに、以下の変更を加えた一例です。

  • ブロックの背景色を透明化
  • タイトルの背景色を直接指定
  • after要素を削除(WordPress側でツールボックス等の表示に使用している為)
  • before要素にafter要素のcontentを統合
/* 追加ブロック //////////////////////////////*/
.wp-block-fla-block-caution {
	position:			relative;
	padding:			1.1rem 0.5rem 0.5rem 1rem;
	margin:				1.3rem 1rem 1rem 1rem;
	font:				0.9em "Hiragino Kaku Gothic ProN", "Hiragino Sans", Meiryo, sans-serif;
	color:				#000;
	background-color:	transparent !important;
	border:				3px solid #955;
	border-radius:		8px;
}
.wp-block-fla-block-caution:before {
	position:			absolute;
	top:				-1.2rem;
	left:				0.8rem;
	padding:			0 0.2rem;
	background-color:	var(--wp--preset--color--background);
	font:				1.5rem "Noto Color Emoji", "Apple Color Emoji", "Segoe UI Emoji", Times, Symbola, Aegyptus, Code2000, Code2001, Code2002, Musica, serif, LastResort;;
	content:			"🚫注意";
}