ハコヅミを作る #19:タイトルバナーとロゴで、トップページを「ハコヅミらしく」する

ハコヅミのトップページは、アプリ名と説明の文字、各画面へのリンクが並ぶだけでした。機能はそろってきたのに、開いたときに「何のアプリか」が見た目で伝わりません。

今回は、タイトルバナー・ロゴ・アイコンを入れます(#97)。Notion の「今後の機能強化」に、ずっと残っていた項目です。


できあがった画面

トップページ。緑のヘッダーの左にロゴとアプリ名。その下に、湖と山を背景に、テント・焚き火・車が描かれたタイトルバナー。バナーの中に、ロゴとアプリ名「ハコヅミ」、キャッチコピー「キャンプの忘れ物を、なくそう。」。バナーの下に、キャンプ・ギア・消耗品・コンテナ・プランの、アイコン付きのメニューが5つ並ぶ

トップページ(パソコンの幅)。バナーの下に、アイコン付きのメニュー

スマホの幅のトップページ。ヘッダー、タイトルバナー、その下にアイコン付きのメニューが3列で並ぶ

スマホの幅では、メニューは3列


Issue の下書きは、一度まるごと書き直した

/issue に渡したメモは「タイトルバナーを作る」だけでした。Claude Code が作った最初の下書きは、絵を SVG で描く案です。色がデザイントークンに付いてくる、画像ファイルを管理しなくて済む、という理由でした。

ただ、絵はこちらで用意したいと思っていたので、3つの画像を作って渡しました。

  • タイトルバナー(景色・ロゴ・アプリ名・キャッチコピー)
  • タイトルロゴ(角丸の四角のアイコン)
  • 5つのアイコン(キャンプ・ギア・消耗品・コンテナ・プラン)

画像を渡すと、下書きはそれを使う形に書き直されました。そのとき、画像を見たうえでの指摘がいくつか付いてきました。

画像 指摘
バナー 絵の中に5つのアイコンも入っている。絵の一部なので押せない。スマホの幅ではバナー全体が高さ 140px ほどに縮むので、押せる大きさにもならない
ロゴ 角丸の四角の外側(四隅)が、透明ではなく黒い
アイコン 5つが1枚にまとまっていて、ラベルの文字も絵の中に入っている

1つ目はもっともだったので、アイコンの入っていないバナーを作り直して渡しました。押せるメニューは、バナーの下に別に置きます。

残りの2つは、画像を作り直さずに、Claude Code の側で加工してもらうことにしました。


画像を、アプリで使える形に加工する

ロゴ:四隅の黒を透明にする

ロゴは、ヘッダーとブラウザのタブに小さく出します。四隅が黒いままだと、緑のヘッダーの上で黒い角が見えてしまいます。

やり方は、画像の縁から、黒い画素をたどって透明にする(塗りつぶしの逆)です。ロゴの中にも黒っぽい線はありますが、四隅の黒とは、クリーム色の地で区切られています。縁からたどれば、四隅だけが透明になります。

// 画像の縁から、条件に合う画素をたどって透明にする
function floodTransparent(data, w, h, match) {
	const seen = new Uint8Array(w * h);
	const stack = [];
	for (let x = 0; x < w; x++) stack.push(x, 0, x, h - 1);
	for (let y = 0; y < h; y++) stack.push(0, y, w - 1, y);
	while (stack.length) {
		const y = stack.pop();
		const x = stack.pop();
		if (x < 0 || y < 0 || x >= w || y >= h) continue;
		const p = y * w + x;
		if (seen[p]) continue;
		const i = p * 4;
		if (!match(data[i], data[i + 1], data[i + 2])) continue;
		seen[p] = 1;
		data[i + 3] = 0; // アルファを 0 に
		stack.push(x + 1, y, x - 1, y, x, y + 1, x, y - 1);
	}
	return seen;
}

角丸の縁には、黒とクリーム色が混ざった画素が1〜2個分あります。そのままだと黒っぽい縁取りが残るので、透明にした範囲を3画素ぶん広げています。

アイコン:1枚から5つに切り出す

アイコンの画像は、5つが1枚に並んでいて、それぞれの下にラベルの文字が入っています。

  1. 背景と違う色の画素がある行・列を調べて、5つの絵の範囲を出す(ラベルの文字の行は、絵の行とは別のまとまりになるので、分けられる)
  2. 絵の範囲だけを切り出す
  3. ロゴと同じやり方で、背景(ごく薄い白)を縁からたどって透明にする
  4. 正方形の透明な台紙の中央に置く

ラベルは絵から外して、HTML の文字で出します。絵の中の文字は、読み上げられず、文字の大きさの設定にも付いてこないためです。

緑の背景の上に並べた、加工後の画像。上の段に、ロゴが3つ(ヘッダー用とタブ用は四隅が透明、ホーム画面用は四角く塗ってある)とキャンプのアイコン。下の段に、ギア・消耗品・コンテナ・プランのアイコン。アイコンは背景が透けて、緑が見えている

加工した画像を、緑の背景に並べて確かめたもの。左から3つ目(ホーム画面用のロゴ)だけは、わざと透明にしていない

透明になったかどうかは、白い背景の上では分かりません。濃い緑の上に並べた画像を作って、目で確かめました。

依存パッケージは増やしていない

加工には、画像を扱う sharp を使いました。ハコヅミは「依存パッケージを増やさない」方針ですが、sharp は Wrangler(の中の Miniflare)がもともと連れてきているものです。新しく入れてはいません。

加工は一度きりの作業なので、スクリプトはリポジトリに入れず、できあがった画像だけをコミットしました。


画面に組み込む

絵の中のアプリ名と、見出し

バナーの絵には、アプリ名「ハコヅミ」が大きく入っています。その下に、今までどおり見出し(h1)の「ハコヅミ」を出すと、同じ文字が2回並びます。

かといって、見出しを消すと、読み上げではページの見出しがなくなります。そこで、見出しは残して、画面には出さない形にしました。

<img
	src={banner}
	alt="ハコヅミ キャンプの忘れ物を、なくそう。"
	width="2000"
	height="727"
	class="mx-auto block h-auto w-full max-w-[90rem]"
/>

<main class="page">
	<!-- アプリ名はバナーの絵に入っているので、見出しは読み上げ用に残して画面には出さない -->
	<h1 class="sr-only">ハコヅミ</h1>
	...
  • バナーには、絵の中の文字と同じ内容の説明(alt)を付ける
  • メニューのアイコンとヘッダーのロゴは飾りなので、alt=""。リンクの名前は、隣の文字(「キャンプ」「ハコヅミ」)になる
  • width と height を指定する。読み込みが終わる前から高さが決まるので、下のメニューが途中でずれない

画像の置き場所

Issue では、画像を static/images/ に置くと書いていましたが、実装では src/lib/assets/ に置いて import する形に変えました。

import banner from '$lib/assets/banner.webp';

import すると、ビルドのときにファイル名にハッシュが付きます(banner.abc123.webp のような名前)。中身が変われば名前も変わるので、ブラウザに長くキャッシュさせても、古い画像が残りません。雛形のままだった favicon も同じ置き方だったので、そろえました。Issue の本文も、変更履歴を付けて直しています。

ホーム画面のアイコンは、透明にしない

ロゴは、ブラウザのタブのアイコンと、スマホのホーム画面に追加したときのアイコンにも使いました。

ホーム画面のアイコン(apple-touch-icon)は、透明のままだと、透明な部分が黒で塗られます。せっかく透明にした四隅が、また黒くなってしまいます。こちらだけは、ロゴの地のクリーム色で塗った画像を別に作りました。

見た目を撮って、2か所を直した

テストが通ったあと、パソコンの幅とスマホの幅で画面を撮って確かめ、2か所を直しました。

  • バナーの幅:画面の幅いっぱいに出すと、幅 1920px の画面では高さが約 700px になり、メニューが画面の外に出る。幅を 1440px までにして、それより広い画面では中央に置いた
  • スマホのメニュー:2列だと、5つ目だけが3段目に残る。3列にして、2段に収めた

テストで確かめたこと

見た目の変更ですが、E2E で確かめられることは確かめています。

  • バナーが、ヘッダーのすぐ下に、画面の幅いっぱいに出る(幅 375px と 1024px)。縦横の比が、もとの画像と同じ
  • メニューの5つから、それぞれの画面に移れる。アイコンの alt は空で、リンクをラベルの文字で探せる
  • ほかの画面には、バナーが出ない
  • 見出し(h1)が1つだけある
  • ロゴの四隅が透明:画面の上でロゴを canvas に描き、四隅の画素のアルファが 0 であることを読む

最後の1つは、「四隅の黒い部分は見えない」という AC を、目だけに頼らず確かめるためのものです。


おわりに

  • 絵の中のアイコンは押せない。押せるメニューは、絵とは別に置く
  • 四隅の黒や背景は、縁からたどって透明にする。透明になったかは、濃い色の上に置いて確かめる
  • 絵の中に文字があっても、見出しと説明(alt)は HTML に残す
  • 画像は import して使うと、ファイル名にハッシュが付いて、長くキャッシュできる

配色は変えていません。画像は明るいミント色、画面は深い緑(#11の「森」)ですが、並べても大きな違和感はありませんでした。色の値はトークンにまとめてあるので、合わせたくなったら、そのとき1か所を直します。

次回は、ギアとキャンプの一覧のカード表示です。

← トップページに戻る