ブログの記事を増やしていくと、本文とは別のところで手が止まります。 サムネイルです。
記事ごとに画像を作れば見栄えは良くなりますが、毎回デザインツールを開く運用は続きません。 反対に、タイトルをそのまま画像へ載せるだけでは、一覧で内容が伝わらず、文字も長くなります。
当初は画像生成AIで一枚ずつ作る案も考えました。 ただ、必要だったのは新しい絵を毎回生み出すことではありませんでした。 記事のどこを短い言葉にし、どの一行を目立たせるかという判断と、同じレイアウトを崩さず描画する処理です。
そこで、AIとプログラムの役割を分けました。 AIは記事を読んで訴求文と強調箇所を決め、Sharpは決められた内容を1200×630の画像へ合成します。 この仕組みを過去の公開記事284件へ適用しました。
判断と描画を分けた構成#
全体の流れは次のようになっています。
記事のfront matterと本文
│
├─ AI:訴求文、改行、強調する一行を判断
│
└─ 生成スクリプト
├─ 背景画像を選ぶ
├─ Sharpで文字とアバターを合成
├─ Cloudflare R2へアップロード
├─ featureimage / imagesを更新
└─ 生成記録へ保存画像生成AIは使っていません。 背景、アバター、文字組みは共通化し、同じ入力から同じ構成を作れるようにしています。
一方、訴求文まで固定ロジックで決めると不自然になりました。 タイトルを機械的に3行へ分割し、常に2行目へオレンジを敷くと、接続途中の言葉が強調されることがあります。 文としては収まっていても、なぜそこが目立つのかを説明できません。
この部分は自動判定を増やすより、記事を読めるAIが都度決めたほうが自然です。 現在は制作規則をskillとして残し、次の順で強調する候補を選びます。
- 読者の予想を変える対比や転換
- 記事から持ち帰れる具体的な判断や行動
- その記事にしかない結果、数字、感情、場面
一般的な主題語しか候補にない場合は、その行を選ばず訴求文そのものを考え直します。 強調した一行だけを見ても意味が通る状態にしてから画像へ配置します。
記事画像の有無で背景を変える#
背景画像は次の順で探します。
- front matterの
featureimage - 記事本文に最初に出てくる画像
- ブログ共通の背景画像
記事に写真がある場合は、その写真を1200×630へ切り抜き、軽くぼかして暗いグラデーションを重ねます。 前面には訴求文と円形の共通アバターを配置します。
最初はぼかしを強くしていました。 文字は読みやすくなったものの、背景が何の写真なのか分かりにくくなりました。 そこでSharpのぼかし量を10から6へ下げ、記事固有の雰囲気が残るように調整しました。 暗いグラデーションは維持しているため、白い文字の視認性も保てます。
記事内に画像がない場合は、淡い回路模様の共通背景を使います。 写真の有無で生成を止めず、どちらも同じ比率と文字配置で仕上げるための分岐です。
1200×630を生成と表示の共通仕様にした#
一度目の実装では、生成画像そのものは問題なくても、ブログの一覧に表示すると左右が見切れました。 生成側と表示枠で縦横比が違っていたためです。
OGP画像は1200×630、比率では40:21に統一しました。 ブログの通常一覧、カード一覧、関連記事も同じ比率に変更し、Open Graphには幅、高さ、MIME typeを明示しています。
画像だけを正しく作っても、表示側の object-cover が別の比率なら端は切れます。
生成、サイト内表示、SNSカードを一つの仕様として扱う必要がありました。
R2とfront matterを自動でつなぐ#
完成画像は一時ファイルとして生成し、Cloudflare R2の次のキーへアップロードします。
posts/<slug>/featured-auto.jpgURLを記事の featureimage と images に設定することで、一覧画像、OGP、Twitter Card、構造化データが同じ画像を参照します。
画像ファイルをGitへ追加しないため、記事数が増えてもリポジトリは大きくなりません。
元画像の取得に失敗した場合は、処理全体を止めず共通背景へ切り替えます。 今回の一括生成では、元画像が404だった2記事がこの経路を通りました。 生成そのものは284件すべて完了しています。
一度作った画像を何度も作らない#
ビルドのたびに284枚を作り直す必要はありません。 R2への書き込みも増え、意図せず以前の画像が変わる原因になります。
生成結果は data/generated-thumbnails.json へ記録します。
記録するのはR2のURL、背景画像の出典、表示した文言、テンプレート版、生成日時です。
通常のビルドでは、記録済みの記事をスキップします。
記事の文言やデザインを明示的に直すときだけ --force を付けて再生成します。
この仕組みによって、自動生成をビルドへ組み込みながら、公開済み画像は不用意に変えないようにしました。
AIに任せた工夫#
今回AIが担当したのは、コードの作成だけではありません。
- 長いタイトルをそのまま使わず、クリック前に内容が伝わる短文へ直す
- どの一行が記事を読む理由になるかを選ぶ
- 背景写真とアバターに文字が重ならないよう改行を調整する
- プレビューを見て、文字切れやオレンジの余白を確認する
- 写真が残るぼかし量と、文字の読みやすさの折り合いを探す
ここを単語の一致や行番号だけで決めると、記事の意味から離れます。 反対に、画像のリサイズ、合成、保存までAIの自由生成にすると、同じ見た目を再現しにくくなります。
文章を読む判断はAIへ寄せ、ピクセルを作る処理はプログラムへ寄せる。 この分け方が、今回の実装で一番しっくりきました。
次の記事からは制作規則を使う#
新しい記事では、AIが本文を読んで thumbnailText を先に決めます。
その後、R2へ送る前にプレビューを開き、オレンジの右端が文字より少し先まで伸びているか、アバターへ重なっていないか、縮小しても意味が通るかを確認します。
自動化しても、最後の一枚を見なくてよいわけではありません。 ただ、毎回ゼロからデザインする必要はなくなりました。
今後は記事を書く作業の中に、短い編集判断と目視確認だけを残します。 それなら、記事が増えても続けられそうです。




