Open16

WordPressのカスタムブロックをAgentic Codingしてみる

ちあきちあき

個人的にやってるアプリとかはよくAIにやらせてるけど、WordPress関連では調べごとをAIに頼むくらいだったので、そもそもWordPressのカスタムブロック作れるのか?を検証してみる。

(言うてちょっとWPのお作法があるだけのReactだからできるだろうとは思ってる)

ちあきちあき

IDEは最近お気に入りのKiroをつかいます。

https://kiro.dev/

事前にChatGPT o3(Claudeはレートリミットでお休み中)と一緒に壁打ちした内容をPre-PRD
としてREADME.mdに書いて、Kiroに仕様書を作ってもらう

Cover-Responsive-Focal

Kiro に渡す初回ブリーフィング(日本語)。WordPress のコア Cover ブロックを拡張し、1 枚の背景画像に対してブレークポイント別のフォーカルポイント(object-position)を設定できるプラグインを実装する。

⸻

1. プロジェクト概要
	•	目的 : Gutenberg Cover ブロックを拡張し、デスクトップ/タブレット/モバイルで被写体が切れないようフォーカスポイントを可変にする。
	•	成果物 : GPL-2.0 or later で公開する軽量プラグイン。スラッグは cover-responsive-focal。

2. 主な機能

#	機能	詳細
1	responsiveFocal 属性	media(文字列)+ x/y(0–1)のオブジェクト配列。
2	インスペクター UI	サイドバーにリピーターを実装。ブレークポイント選択+FocalPointPicker。PC/SP プリセットをデフォルト生成。
3	動的 <style> 生成	render_block フィルターで CSS 変数 --fp-x / --fp-y をメディアクエリ内に出力。
4	画像は 1 枚のみ使用	既存 <img> を再利用。追加マークアップなし、CLS 0。
5	後方互換	responsiveFocal が空ならコアの focalPoint 挙動にフォールバック。

3. 技術スタック & ツール
	•	WordPress 6.5 以上
	•	@wordpress/scripts(Babel + Webpack)
	•	React 18 / Gutenberg Packages
	•	PHP 7.4+
	•	SCSS(任意)

4. ディレクトリ構成(案)

cover-responsive-focal/
├─ build/               # ビルド成果物
├─ src/
│  ├─ index.js          # registerBlockType フィルター
│  ├─ inspector.js      # UI
│  ├─ save.js           # save 要素オーバーライド
│  └─ style.scss        # 共通 CSS
├─ plugin.php           # プラグインエントリ
├─ package.json
├─ composer.json        # (必要なら)
└─ readme.txt           # wp.org 用

5. 属性スキーマ

responsiveFocal: {
  type: 'array',
  default: [], // [{ media:'(max-width:767px)', x:0.60, y:0.40 }, …]
},

※ 既存属性(url, id, focalPoint 等)はそのまま。

6. JavaScript 概要

// index.js(一部)
import { addFilter } from '@wordpress/hooks';

addFilter(
  'blocks.registerBlockType',
  'crf/extend-cover',
  ( settings, name ) => {
    if ( name !== 'core/cover' ) return settings;
    settings.attributes.responsiveFocal = { type: 'array', default: [] };
    return settings;
  }
);

	•	InspectorControls でのリピーター UI と save.js の実装は Kiro タスク。

7. PHP 概要

/* plugin.php */
add_filter( 'render_block', 'crf_render_block', 10, 2 );
function crf_render_block( $content, $block ) {
    if ( 'core/cover' !== $block['blockName'] ) return $content;
    $attrs = $block['attrs'];
    if ( empty( $attrs['responsiveFocal'] ) ) return $content;
    $id = esc_attr( $attrs['dataFpId'] ?? wp_unique_id( 'crf-' ) );
    $rules = '';
    foreach ( $attrs['responsiveFocal'] as $row ) {
        $rules .= sprintf( '@media %s{[data-fp-id="%s"]{--fp-x:%s%%;--fp-y:%s%%;}}',
            $row['media'], $id, $row['x']*100, $row['y']*100 );
    }
    return $content . "<style id=\"$id\">$rules</style>";
}

8. CSS スニペット

.wp-block-cover__image-background {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--fp-x, 50%) var(--fp-y, 50%);
}

9. Kiro へのタスク一覧
	1.	ディレクトリスキャフォールド & package.json 生成
	2.	responsiveFocal 属性追加(JS)
	3.	InspectorControls リピーター UI 実装
	4.	save.js で data-fp-id + インライン変数出力
	5.	PHP render_block フィルター完成
	6.	SCSS コンパイル設定
	7.	wp-env で動作確認 & ESLint/PHPCS 通過
	8.	README(日本語)作成

10. 受け入れ基準
	•	responsiveFocal 未設定時はコア動作と同一。
	•	モバイル LCP < 2.5s、CLS ≤ 0.1。
	•	@wordpress/eslint-plugin/recommended & WordPress-Core コーディング規約準拠。

11. 今後のロードマップ
	•	v0.2 : デスクトップ/タブレット/モバイルの自動プリセット追加
	•	v0.3 : フォーカル未指定時のサーバーサイドスマートクロップ(Imagick/OpenCV)
	•	v1.0 : WP.org 登録、国際化、GitHub Actions CI

⸻

以上で Kiro への初回ブリーフ完了
ちあきちあき

さすがに @wordpress/create-blockで開発環境つくってーは無理そうかなぁ

ちあきちあき

@types/wordpress__block-editorとかの型情報もちゃんと取ってきてくれてるから、ベースは作ってもらう->確認して人間が手直しするで十分作れそう

ちあきちあき

テストとかもちゃんと書いてくれて感動・・・。正直カスタムブロックのテストの書き方どう書いたらいいんだろう?って状態なので、サンプルになりうるもの書いてくれるだけでありがて〜

ちあきちあき

「ここはなんでこうしてるの?」を深掘りしながら、納得できなかったら直していくスタイルですすめる

ちあきちあき

同じようななぜなぜ攻撃がだるくなってきたらCLAUDE.mdに方針として残す

ちあきちあき

「なんで?」ってシンプルに聞くと教えつつコード直すから、壁打ち気味にやりたいときは「直して欲しいって意図じゃなくて先にどうしてこうなってるのか解説してほしい」的に書くと、(セッションが違うからだと思うけど)推察して教えてくれて、現在の推奨がどうなっているか、どうするのがよさそうか(もしくはこのままでいいか)までいってくれる

ちあきちあき

たとえばテストのfireEventuserEventの使い分け。
大体fireEvent使われがちだな〜っていう体感があるけど、WP的にfireEventの方がまだメジャーなのかもしれないということで調べてもらった。

結果、fireEventuserEvent両方使われてるけど、Testing Libraryが推奨してるuserEventで統一しましょうとなってコード変更。でも、Range InputはuserEventが使えなかったからfireEventを使用した。まあハイブリッドアプローチ。

  userEventの制限事項

  - Range Input: user.clear()やuser.type()が使用できない
  - 解決策: 実際のユーザー体験に近いテストが困難な要素では、fireEventを部分的に併用
ちあきちあき

「サブタスクごとにブランチ切ってプルリクエストして」は失敗しがち

ちあきちあき

調べてないしやってないけど、tasks.md のタスクの最後に「新しいブランチを作成してプルリクエストを行う」とか書いてたらもしかしてうまくいくのだろうか………

ちあきちあき

プロトタイプをつくる->見てもらって方向性のすり合わせするっていうフローが個人的にすごくやりやすいかも。

ちあきちあき

凝らなくていいのに「このケースだとバグる?」「min-width とmax-width混在したときのテストってどうしようかな」とか考え始めて一生終わらない様相を見せ始めたけど、すり合わせ通話で モバイルとデスクトップ固定にする という方針を決められた