タグ
白いカードにテラコッタ色のClaudeの星形ロゴと黒のロゴタイプ

AIエージェントと5ラウンドでブログのロゴを作る — 手元に残ったのは/logoスキルと3本のスクリプト

AIエージェントとブログのロゴとコードフォントを作り直した記録です。壊れたSVGの字形、正規表現によるバウンディングボックス、間違った漢字、そしてそこから生まれた/logoスキルを扱います。

目次

はじめに

長いあいだ気に入っていなかったロゴを直そうと思って、作業を始めました。プロジェクトを始めた初日にツールが自動生成した、テキストだけのマークです。それについて、誰も判断を下していませんでした。

AIエージェントとロゴを設計する作業は、一発で終わるものではありません。私もそうしたいとは思っていませんでした。知りたかったのは、そのループ自体をClaude Codeのスキルとして書き留める価値があるかどうかです。エージェントが候補を出し、私が見て、削って、理由を言い、納得するまでもう一周する。エージェントはそのラウンドの中で5回、自信を持って間違えましたが、そのどれも無駄だったとは思っていません。 1つ1つの誤りがルールを1つ生み、そのルールは今、/logoスキルと3本のスクリプトになっています。次のマークはそこから始められます。

出荷したのは、桜の字を入れた朱色の印と、その隣にFrauncesで組んだ「oharu」、そして記事ページだけに効くJetBrains Monoです。本記事では5つのラウンドと、それぞれを見つけるのに何が必要だったか、そしてそこから生まれたスキルを追います。

ブログにデザインがなく、そのまま使い続けていた

ファビコンはこうなっていました。

以前のファビコンを3つのサイズで角の丸い青い四角の中央に白い小文字の o が入ったマークを、128・32・16ピクセルで並べたもの。文字はどのサイズでも判別でき、16ピクセルでは中央に小さな明るい形のある青い四角に見える。ooo128px32px16px
角の丸いrectと、Helvetica,Arial,sans-serifを指定した<text>が1つ。ファビコンが実際に使われる3つのサイズで並べています。文字は訪問者のOSが用意したものです。

ロゴではありません。そのまま使い続けていたプレースホルダーです。ヘッダーにはマークすらなく、サイトタイトルがテキストで置いてあるだけでした。

タイポグラフィも同じ話です。

src/styles/global.css
--font-sans: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, …;
--font-mono: ui-monospace, 'SF Mono', SFMono-Regular, Menlo, Consolas, …;

純粋なシステムスタックです。つまりフォントがつまらなかったのではありません。フォントがなかったのです。 訪問者は全員、自分のOSを見ていました。これは正当なトレードオフではありますが(バイト数ゼロ、レイアウトシフトゼロ)、誰かが下した判断ではありませんでした。

誤り1: 1つのアイデアの24バリエーション

まず幅を求めました。アイコンのみ、ワードマーク、組み合わせマークにまたがる24案を、128px・32px・16pxで同時に見られるギャラリーに出力してほしい、と依頼しました。

24案は出てきました。しかしすべて同じロゴでした。

8つのアイコン案が並んだグリッド。どれも同じ線幅、同じ丸い端点で描かれた円やリングの細い青いアウトライン

これはシートのアイコン側半分です。アパーチャ、軌道、ターミナル、円弧、鳥居、スクワークル、プロンプト、分割。1つの絵に8つの名前が付いているだけでした。すべてがモノラインの幾何ストロークで、円と円弧、均一な線幅、丸いキャップ。アイコンでもワードマークでも骨格は共通です。その骨格は言語モデルが真っ先に手を伸ばすデフォルトであり、インターネット上のAIロゴ生成サービスのハウススタイルそのものです。私は幅を求めて、8つの帽子をかぶった繰り返しを受け取りました。

解決策は、より良いプロンプトではありませんでした。先に本物の仕事を見に行くことです。6つのアイデンティティをスクリーンショットに撮り、実際に観察しました。

参照先 何をしているか ラウンド1がやっていたこと
LinearAstro ベタ塗り。マークは小さく控えめ すべてストローク。16pxで潰れる
Rauno FreibergJosh Comeau マークがそもそもない。 文字と、覚悟を決めた色が1つ マークは必須だと決めつけていた
Maggie Appleton ディスプレイセリフ。 書くためのサイトだから テンプレートのデフォルトである幾何学サンセリフ
Anthony Fu 個人のモノグラム、手書きの文字 純粋な幾何学から「個人的」は出てこない

ラウンド2は本当に違うものが返ってきました。今回は制約が実在する場所から来ていたからです。

16案が、エディトリアルセリフ、グロテスク、朱色の判子、色付きの文字、モノグラムに分類されて並んでいる。1つではなく5つの異なるアイデアであることが見て取れる

この教訓はロゴの外にも広がります。AIにN案を求めれば、1つの分布からN個サンプリングされたものが返ってくるのです。幅は外側から課すしかなく、本物の仕事を見ることが、その「外側」がどんな形をしているかを知る方法です。

誤り2: 一度もレンダリングされなかった字形

ラウンド1は手で組んだジオメトリで、一度もレンダリングされないままギャラリーに入りました。3つのバグが目の前に転がっていました。

いちばんひどかったのはuです。

M9,49 L9,99 A41,41 0 0 0 91,99 L91,140

ペンの動きとして読んでください。左のステムをx-heightからy=99まで下ろす。ボウルの円弧で(91,99)まで渡る。そこからベースラインまで線を引く。円弧が終わるのがy=99なので、右のステムは99から140までしかありません。上半分がまるごと欠けています。 この字は右側が伸びきらないuとしてレンダリングされます。

修正はサブパスを分けることです。

M9,49 L9,99 A41,41 0 0 0 91,99 ← bowl
M91,49 L91,140 ← full-height right stem

「oharu」の文字が2つ。上は最後のuの右のストロークが高さの半分で止まっている。下は同じ語で、ストロークが隣の文字と同じx-heightまで伸びている

残る2つも同じ種類の誤りでした。y=160に置いて0.26倍したサブタイトルは、アセンダが160 + 0.26 × (−700) = −22に来ます。真下に置いたはずの語のベースラインより上です。その結果thblが名前を突き抜けました。もう1つは、700ユニットの文字の隣に220ユニットの高さで描かれたマークで、ロゴではなく行き場のない箇条書きの点に見えました。

どれもブラウザでは一目瞭然で、パスデータの上では見えません。ジオメトリは推論されただけで、一度も見られていなかったのです。

誤り3: エージェントが決めつけた漢字

ラウンド2から出てきたいちばん強いコンセプトは判子、つまり個人の印でした。ブログという存在に近いものです。朱色、1文字、白抜き。

名前が「oharu」なので、エージェントは春(haru)を使いました。見た目は良く、あと少しで作られるところでした。

そこでエージェントは質問しました。誰かのアイデンティティに文字を入れるという判断を、推測のまま黙って進めるものではないからです。私は訂正しました。「oharu」は桜明です。春ではまったくありません。

この回答はグリフだけでなく、デザインそのものを変えました。桜明は2文字であり、2文字は16pxのファビコンでは生き残れません。あのサイズでは10画の漢字はテクスチャであって、文字ではないからです。

朱色の印の案が6つ。それぞれ大きく表示され、さらに32ピクセルと16ピクセルでも並んでいる。16ピクセルでは2文字の案は輪郭のない塊になり、1文字の丸印だけがまだ読める

16pxのレンダリングが決着をつけます。縦に積んだ桜明はこの名前として正しい判子であり、ブラウザのタブでは最下位でした。輪郭だけの朱文版は完全に消えます。そのため印は丸型・1文字の桜になり、この選択は好みではなく画数で決まりました。

一般化するとこうなります。AIは、あなたに関する推測の上に、自信があって、もっともらしくて、よく仕上がった成果物を平気で作ります。 その推測は出力を見ても分かりません。質問して初めて見えるものです。

誤り4: 正規表現で計算したバウンディングボックス

本番用のアセットでは、手で描いた文字をやめ、実在の書体からアウトラインを焼き込む方式に移りました。ワードマークにはFraunces、桜にはNoto Serif JPを使い、fontToolsとHarfBuzzで処理します。正しいカーニングとコントラストがパスデータとして凍結されるので、ワードマークはウェブフォントのリクエストもフォールバック表示のちらつきもないジオメトリとして配信されます。

グリフを配置するには、インクの実際の境界が必要です。スクリプトはパス文字列からすべての数値を抜き出し、それをx、yの交互の組として扱っていました。

これは誤りで、しかも静かな誤りです。SVGPathPenHVの短縮形を出力し、これらのコマンドが持つ座標は1つだけです。水平線が1本あるだけで、それ以降のすべての数値のパリティが反転します。

このバグは、少しずれて見えるだけの状態でギャラリー2ラウンドを生き延びました。決定的になったのは、約3200ユニット幅になるはずのロックアップが1802で返ってきたときです。

修正はテキストの解析をやめ、ライブラリに測らせることです。

outline.py
from fontTools.pens.boundsPen import BoundsPen
bounds = BoundsPen(glyph_set)
for info, pos in zip(buf.glyph_infos, buf.glyph_positions):
xform = (scale, 0, 0, -scale, (x + pos.x_offset) * scale, 0)
glyph_set[name].draw(TransformPen(pen, xform))
glyph_set[name].draw(TransformPen(bounds, xform)) # same transform

ワードマークの実際の境界は2456.4 × 758.6でした。正規表現は最初のギャラリーからずっと、自信たっぷりに別の値を返していたことになります。

取り上げる価値があるのはこの失敗の形です。クラッシュしなかったのです。 もっともらしい出力を作り、人間がちらっと見て2度承認しました。

誤り5: 全ページに入ったプリロード

セッションのもう半分は、コードに専用の書体を与えることでした。コードブロックは技術ブログが実際に読まれる面ですが、こちらはMenloでレンダリングされていました。本文と同じ「誰も決めていない」状態です。

JetBrains Mono Variable、Latinサブセットのみ、40KB、固定パスにセルフホストしてURLでプリロードできるようにしました。これは日本語ページと中国語ページでは一切コストになりません。 コードはLatinなので、CJKスタックには手が入らず、CJKフォントが取得されることもありません。

プリロードは共有レイアウトに入れました。それが誤りである理由は、ブラウザが教えてくれました。

The resource http://localhost:4321/fonts/jetbrains-mono-latin.woff2 was
preloaded using link preload but not used within a few seconds from the
window's load event. Please make sure it has an appropriate `as` value and
it is preloaded intentionally.

トップページ、タグ一覧、そしてCJKの2つのインデックスページには、コードが1つもありません。それぞれが何も描画しないために40KBを取得していました。 記事ページだけが立てるpropで囲った結果はこうです。

ページ rel="preload"のリンク数
dist/index.html 0
dist/ja/index.html 0
dist/tags/index.html 0
dist/blog/<article>/index.html 1

コンソールの警告1つで、実際の削減が1つ。5つのうち、ツールが自分から申告してきた誤りはこれだけです。 残りはすべて、誰かが見に行ったからこそ表に出ました。

ラウンドが残したもの: /logoスキル

1つ1つの誤りが、次の実行では飛ばせないルールを1つ生みました。それがそのままスキルの非交渉事項であり、飛ばしたときの被害が大きい順に並んでいます。

ルール どの誤りから来たか
何かを生成する前に、本物の仕事を研究する 1つの分布から出てきた24案
誰かに見せる前に、レンダリングして自分の目で見る ステムが半分しかないu
16pxで判断する ブラウザのタブで滲む桜明
その人に関する推測を焼き込まない
手描きの字形ではなく、実在の書体のアウトラインを使う 手組みのジオメトリと、それを測った正規表現
色は理論ではなく比較で決める 赤い印の隣に置いた青いワードマーク
どのゲートでも、選ぶのはユーザー ループそのもの

最後の1つが、残りを機能させています。スキルには、提示して、理由付きで推薦して、そこで止まるように書いてあります。意見は役に立つが、決定はあなたのものではない、と。本記事のすべてのラウンドは、私が選ぶゲートで終わっています。 5つの誤りが、推測の上に立った完成品ではなく半日で済んだ理由もそこにあります。

機械的な部分は3本のスクリプトが担います。gallery.mjsはコンタクトシートを組み立ててローカルのポートで配信し、outline.pyはfontToolsとHarfBuzzで書体のアウトラインをパスデータに焼き込み、build-brand-lockup.mjsはファビコンとヘッダーコンポーネントを出力するお手本です。コンセプトとシートはスクラッチのディレクトリに書き、リポジトリには入れません。デザインのラウンドは大半がボツであり、リポジトリはボツの置き場ではないからです。

誤り5からは、ここに入るルールが出ませんでした。コードのないページで発火するプリロードはレイアウトのバグであってデザインのバグではなく、マークではなくセッションのフォント側に属する話だからです。

出荷したもの

桜を入れた朱色の丸印と、その隣にFrauncesで組んだ「oharu」。SVGは1つではなく2つです。印はワードマークのインクの高さより意図的に低く作ってあるため、ロックアップ全体を正方形に切り抜いて狭幅版を作ることができないからです。50rem未満では印だけが表示されます。これは以前のテキストワードマークが抱えていたレイアウトの問題、つまりスマートフォンでタイトルが検索コントロールと場所を奪い合う問題を、副産物として解決しました。

色の判断が1つ、理屈ではなく目で決まりました。サイトのアクセントは#1d4ed8、印は#d8452aです。この2つは色相環でほぼ正反対にあり、青いワードマークの隣に赤い印を置くと目に見えて喧嘩します。解決策は別の赤ではありませんでした。 ワードマークにはテーマの文字色を使わせ、青はリンク用に残す。こうすれば2つが隣り合うことがありません。

コストは見積もりではなく実測です。

ワードマークのパスデータ 3569バイト(5145から整数丸め)
印のパスデータ 1677バイト
favicon.svg 1878バイト
JetBrains Mono、Latinサブセット 40KB、記事ページのみ
/ja/でのウェブフォントリクエスト 1件、CJKフォントはなし

astro checkは53ファイルに対して0 errors、0 warnings、0 hintsを報告しています。

まとめ

誤りが5つ。そのどれ1つとして、コードについて推論することでは捕まりませんでした。

ありきたりなコンセプトは、ありきたりだと見えるようになるために本物の参照を必要としました。壊れた字形はブラウザを必要としました。間違った漢字は質問を必要としました。バウンディングボックスのバグは、気づけるほど不自然な数値を必要としました。無駄なプリロードはコンソールを必要としました。

このリストこそが、これをプロンプトではなくループとして回す理由です。磨き上げた24案が数分で出てくるようになると、量を幅と取り違えるのも、推測の上に立った自信満々の成果物を受け入れるのも、とても簡単になります。その重しになるのは、より良いプロンプトではありません。実際にレンダリングし、実際に計測し、私が持っていてエージェントが持っていない事実に依存するときには質問することです。

5つのどれも、当時も今も無駄には見えません。1つ1つがルールになって返ってきたからです。次のマークは白紙のプロンプトではなく、7つのルールと3本のスクリプトから始まります。 そこが、残す価値のある部分です。

この記事をシェア