| 項目 | 値 |
|---|---|
| 公開記事数 | 20 本(5/5 公開 1 + 5/6〜5/24 予約 19) |
| 自動生成インフォグラフィック | 60 枚(カバー + 本文 2 枚 × 20 記事) |
| インフラ月額 | 0 円( 無料枠 + Resend 無料枠) |
| デプロイ手段 | vercel --prod 1 行 |
| 投稿手段 | MDX ファイルを置くだけ |
| 平均ページ表示速度(warm キャッシュ) | 49〜59ms |
| ローカル dev → 本番デプロイの差 | dev 11,640ms → 本番 49ms(約 240 倍 高速) |
7 つの落とし穴 — 実装中に転んだ場所
ここからが本題です。15 時間で 20 記事 + 60 画像を本番化するまでに、7 箇所で立ち止まりました。それぞれ、転んだ理由と突破方法を共有します。落とし穴 1: gray-matter の date は string ではなく Date オブジェクト
予約投稿(公開日まで非表示)を実装したとき、なぜか すべての記事が消える 現象が発生しました。 原因は frontmatter の date 解析。YAML でdate: 2026-05-05 と書くと、gray-matter は JavaScript Date オブジェクト として返します。文字列化すると "Tue May 05 2026 09:00:00 GMT+0900" のようになり、"2026-05-05" との単純比較が壊れます。
// "Tue May 05..." > "2026-05-05" → 辞書順で true("T" > "2")
// → 全記事が「未来日付」扱いでフィルタされる
突破: 読み込み時に JST に揃えてから ISO YYYY-MM-DD にするヘルパーを 1 つ追加。本番環境のタイムゾーンが UTC である件もここで吸収しました(Vercel は UTC、JST との 9 時間ズレで日付が 1 日ズレるのは別の罠)。
落とし穴 2: Reveal アニメーションで本文が永久に opacity 0
framer-motion の
useInView を使ったフェードイン演出で、JS 失敗時や IO 不発火時に文字が完全に消える 問題が発生しました。
特に複合的な事故が起きたのは、本文中間に CTA バナーを差し込む機能を追加した瞬間。
それまで
の中身は子 div 1 つでしたが、CTA 挿入で前半 + バナー + 後半の 3 つの子 div に分割。Animator の document.querySelector(".article-body > div") は最初の 1 つしか取らないため、後半の文字が永久に opacity 0 のまま で表示されませんでした。
突破: 設計を反転しました。デフォルト opacity 1、JS が走った時だけ .ab-pending クラスを付けてフェード演出を有効化。「壊れても文字は読める」を最優先に。
落とし穴 3: 画像 AI でインフォグラフィックは作れない
最初は Codex(画像生成 AI)でインフォグラフィックを作ろうとしました。プロンプトを 60 個書いて、画像 AI に投げる。
結果は壊滅的でした。
私が書いたプロンプトの中身(CSS 仕様・カラーコード・レイアウト指示)が、そのまま画像内のテキストとして配置されたのです。「#ffffff / #c8c8d0」「縦長フローチャート的にステップ 01→02→03→04」という 人間向けの仕様書 を、AI が「画像に書き込む文字」と解釈しました。
根本問題は、画像 AI が「正確なテキスト・数字・レイアウト」が苦手という技術的限界です。マーケ用インフォグラフィックには相性が悪い。
突破: コード生成方式に切り替え。Satori(JSX → SVG)+ resvg-js(SVG → PNG) で 4 種類のテンプレ(cover / flow / grid / comparison)を実装。JSON で「データ + テンプレ種別」を指定すれば、コマンド一発で 60 枚を 30 秒で生成できる構造に。

コード生成インフォグラフィックの 4 テンプレート
落とし穴 4: Satori が calc() を解釈しない
JSX で書いた width: "calc(33.333% - 12px)" が 無視される ことに気づいたのは、出力が縦 1 列に並んだ時です。
Satori は CSS の完全実装ではなく、サブセットのみ対応しています。calc() はサポート外。background-clip: text、box-decoration-break、mask-image も非対応です。
突破: JS 側でピクセル計算してから渡す。
const totalW = 1040;
const gap = 16;
const cardW = Math.floor((totalW - gap * 2) / 3); // 3 列幅を実数で
事前に Satori の CSS サポート表を確認すべきでした。動作検証フェーズをテンプレ実装より先に置くのが教訓です。
落とし穴 5: ブロックエディタが server component で動かない
WordPress 風の編集 UI を求める声に応えて BlockNote を導入。1 行目で window is not defined で SSR がクラッシュしました。
ブロックエディタ系(BlockNote / TipTap / Lexical)は クライアント専用 です。Next.js 16 では ssr: false を server component から指定できないため、間にクライアントラッパーを挟む 必要があります。
// editor-loader.tsx (client)
"use client";
import dynamic from "next/dynamic";
export const Editor = dynamic(() => import("./editor"), { ssr: false });
// page.tsx (server) — server から Editor を import
import { Editor } from "./editor-loader";
- AI と並列分担: 私(人間)が設計・整合性管理、AI が実装・量産。両者の境界を明確にする。AI に判断を委ねず、人間が判断を委ねない。
- データ駆動で量産: 20 記事も 60 画像も、JSON / MDX のデータが先。コードはテンプレ化すれば、残りはデータを流すだけ。
- 完成度より構造: 60 画像を細かく磨くより、テンプレを 4 つに絞り込んで全部に同じ温度で適用。微調整は本番反映後に効く順で。
- 運用も同じ仕組みに乗せる: AI が将来も使う仕組みを、人間が書きやすい形に揃える(MDX を選んだ理由)。
よくあるご質問
Q1.なぜ WordPress を使わなかったのですか?
A.記事を書く側(人間 + AI)と、表示する側(Web)の摩擦をできるだけ減らしたかったためです。MDX ファイルを git に置くだけで公開される構造にすれば、AI に「ファイルを書け」と指示するだけで完結します。WordPress は GUI 編集の柔軟さがありますが、AI 自動化との相性は MDX の方が圧倒的に良いです。
Q2.開発期間は本当に 1 日ですか?
A.コア機能(MDX 読込・予約投稿・インフォグラフィック生成・CTA 統合)の実装は 1 日で動きました。ただし「20 記事のコンテンツ作成」と「60 画像のテンプレ磨き込み」を含めて、実時間ではおよそ 12〜15 時間です。AI(Claude Code)と並列でファイル生成しなければこの速度は出ません。
Q3.画像生成 AI は使わなかったのですか?
A.最初は Codex(画像生成 AI)でカバー画像とインフォグラフィックを作ろうとしました。しかし「正確なテキスト・数字・レイアウト」が必要なインフォグラフィックは画像 AI が苦手な領域でした。最終的には Satori(JSX → SVG → PNG)でコード生成する方式に切り替えました。詳細は本文の落とし穴 3 をご参照ください。
Q4.個人ブログでも使える構成ですか?
A.はい。Next.js + MDX + Vercel で月額 0 円で動きます。ホスティングコストはアクセス急増時のみ発生するため、月数千 PV 規模なら無料枠内です。EXBANK のブログもこの構成です。
Q5.コードはオープンソース化しますか?
A.現時点では予定がありません。ただし、Satori のテンプレ生成と予約投稿の実装パターンは別記事で詳細解説する予定です。
Q6.AI が書いた文章を SEO 的にどう扱っていますか?
A.Google は「AI 利用の有無は順位に影響しない、品質だけ見る」と公式に述べています。当方ブログは AI が下書き → 編集部が事実検証 + 数値追加 + 文体調整というワークフローで、最終的な「品質責任」は人間が持つ運用です。
