ハコヅミを作る #19:タイトルバナーとロゴで、トップページを「ハコヅミらしく」する
ハコヅミのトップページは、アプリ名と説明の文字、各画面へのリンクが並ぶだけでした。機能はそろってきたのに、開いたときに「何のアプリか」が見た目で伝わりません。
今回は、タイトルバナー・ロゴ・アイコンを入れます(#97)。Notion の「今後の機能強化」に、ずっと残っていた項目です。
できあがった画面

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

スマホの幅では、メニューは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枚に並んでいて、それぞれの下にラベルの文字が入っています。
- 背景と違う色の画素がある行・列を調べて、5つの絵の範囲を出す(ラベルの文字の行は、絵の行とは別のまとまりになるので、分けられる)
- 絵の範囲だけを切り出す
- ロゴと同じやり方で、背景(ごく薄い白)を縁からたどって透明にする
- 正方形の透明な台紙の中央に置く
ラベルは絵から外して、HTML の文字で出します。絵の中の文字は、読み上げられず、文字の大きさの設定にも付いてこないためです。

加工した画像を、緑の背景に並べて確かめたもの。左から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か所を直します。
次回は、ギアとキャンプの一覧のカード表示です。