ハコヅミを作る #22:見出しと一覧の見た目を、サンプル画像を見てから直す
前回(#21)でフォントを変えたら、今度は一覧の見た目が気になってきました。機能は足りているのに、どこか事務的です。
今回は、見た目の手直しを3つの Issue(#104・#106・#108)で進めた話です。どれも、Issue を作る前に、サンプル画像を見て決める進め方にしました。
進め方:先に画像、あとで Issue
見た目の変更は、言葉で仕様を書いても、できあがりが想像と違うことがよくあります。#14では、PR を出してから見た目を2回直しました。
今回は、/issue のメモに「サンプル画像を提示して」と書いて、順番を入れ替えました。
- メモを渡す(「こうしたい」と、参考の画像があればそれも)
- Claude Code が、今の画面に見た目だけを仮に当てて撮った画像を、案ごとに並べて出す
- 画像を見て、案を選ぶ
- 選んだ内容で Issue を作る →
/implement
2 の画像は、手元で動かしたアプリのページに、スタイルや HTML を一時的に差し込んで撮ったものです。コードは変えていません。作るのは数分で、捨てても惜しくありません。
この記事の「今/案A/案B」の画像は、そのときのサンプルです。データは、#7で取り込んだ実際のギアと、記事用の架空のキャンプ場・消耗品です。
見出しを、線の上に乗ったタブにする(#104)
カテゴリやコンテナの見出しは、緑の文字と下の線だけでした。項目の文字に埋もれて、区切りが分かりにくい。参考にしたい見出しの画像(線の上に、色を塗った札が乗っている形)を渡して、サンプルを作ってもらいました。

案A:タブ+今と同じまっすぐな線(左:ギアの一覧、右:持ち物チェック)

案B:タブ+両端が下がった線(渡した参考の画像に近い形)
案B は参考の画像に近い形ですが、項目がたくさん並ぶ一覧では、少しにぎやかです。案A にしました。
サンプルを作る段階で、気づいたことが1つあります。持ち物チェックでは、コンテナの見出しの左に、一括チェックのチェックボックスがあります(#9)。緑のタブの中に入れると、緑のチェックボックスが背景に溶けて見えなくなります。タブの中だけ、白いチェックボックスにしました。言葉だけで仕様を書いていたら、実装してから気づいたはずです。
実装は、見出しの見た目を1つの部品(SectionHeading)にまとめただけです。これまでの見出しの部品(Section)の中身を差し替えたので、それを使っている画面は、すべて同じ見た目に変わりました。プランの画面のカテゴリの見出しは別の作りだったので、同じ部品を使うように直しています。
見た目の微修正・その1(#106)
タブを本番の前に開発環境で使ってみると、今度はタブが目立ちすぎるのが気になりました。あわせて、ギアとキャンプのリストも直します。
タブを少し小さく

タブの大きさ(左から、今/案A 16px/案B 14px)
「若干小さく」とメモに書きましたが、どれくらいかは、見ないと決められません。項目の文字と同じ大きさになる 案A(16px) にしました。
ギアのリスト:★をうしろへ

ギアのリストの並び(左:今、右:名称 → メーカー → コンテナ → ★)
おすすめ度の★(#13)は、名称のすぐうしろに置いていました。★の数で行の長さが変わり、メーカーやコンテナのバッジの位置がそろいません。★をいちばんうしろに回しました。
キャンプのリスト:1件を1行にまとめる
いちばん変わったのは、キャンプのリストです。

キャンプのリスト(左から、今/案A:2〜3行にまとめる/案B:左に日付の札)
左の「今」は、1行に全部の情報を詰めた、下線付きのリンクです。メモには「ちょっと事務的な感じなので、もう少し洗練したイメージにして」としか書いていません。それでも、案が2つ並べば、選ぶことはできます。
どちらの案も、次のように変えています。
- キャンプ場の名前・日付・プランを、3段に分ける
- 日付に、曜日と泊数を足す(「2026/10/10(土)〜10/11(日)・1泊」)
- プランは、バッジで出す
- 進み具合は、右に数と細い棒。全部済んだら「積み込み済み」
- 行全体を押せるようにする
左に月日の札が付く 案B にしました。スマホの幅では、札の分だけキャンプ場の名前が折れやすくなりますが、いつのキャンプかが一目で分かります。
落とし穴:同じ名前のラベル
実装では、これまでの E2E が1件落ちました。一覧からキャンプを押して、キャンプの画面で進み具合を確かめるテストです。エラーは、こうでした。
Error: strict mode violation: getByLabel('進み具合') resolved to 182 elements
キャンプの画面の進み具合には、aria-label="進み具合" を付けています。新しいリストの行の進み具合にも、同じラベルを付けました。テストは、行を押したあと、画面が切り替わるのを待たずに「進み具合」を探します。ふだんは、キャンプの画面に1つだけ見つかるまで待ってくれます。ところが、一覧にも同じ名前の要素がたくさんあるので、画面が切り替わる前の一覧のほうで見つかってしまい、「1つに決まらない」というエラーになりました。
一覧の行のほうは、ラベルではなく、読み上げ用の文字(画面には出ない「進み具合」)に変えました。読み上げでは今までどおり「進み具合 15 / 45」と読まれ、テストがラベルで探したときには、キャンプの画面の1つだけが見つかります。
同じ名前を2つの画面に付けると、画面をまたぐテストが取り違える。見た目の変更でも、こういう形でテストに響きます。
見た目の微修正・その2(#108)
続けて、コンテナとプランの一覧、消耗品の残りの表示です。
コンテナ一覧・プラン一覧

コンテナ一覧(左から、今/案A:アイコン付きの行/案B:アイコン付きのタイル)
名前のリンクが並ぶだけだった一覧に、#19で用意したアイコンと、中身の件数を足しました。キャンプのリストと作りがそろう 案A にしています。プラン一覧も同じ形です。
消耗品の残りの表示

消耗品の残りの表示(左から、今/案A:色付きの札/案B:色の丸+文字)
「残りなし」を見落とさないことが目的なので、はっきり出る 案A にしました。どちらの案でも効いているのは、右端にそろえたことです。今は名前のすぐうしろにあるので、行ごとに位置がばらばらで、目で追えません。
持ち物チェックの「残りなし」(#16)は、変えていません。持ち物チェックの行は、チェックボックスやバッジで込み合っていて、札を足すとうるさくなるためです。
コンテナの編集画面

コンテナの編集画面(左:今、右:中身も白いカードの中に入れる)
これは、タブにしたことで目立つようになった不ぞろいです。この画面だけ、中身の一覧が白いカードの外に出ていました。
落とし穴:件数が、全部 0 になる
コンテナ一覧に出す件数は、1回の問い合わせで数えたいので、サブクエリで書きました。
gearCount: sql<number>`(select count(*) from ${gears} where ${gears.containerId} = ${containers.id} ...)`
ところが、足した E2E が落ちます。ギアを2つ入れたコンテナが、「ギア 0 件」と出ていました。
原因は、Drizzle が出す SQL です。join のない問い合わせでは、Drizzle は列をテーブル名なし("id")で出します。上の ${containers.id} も、SQL の中では "id" になります。サブクエリの中で "id" と書くと、SQLite は、まず中のテーブル(ギア)の id として読みます。つまり「ギアのコンテナの id が、ギア自身の id と同じ」という、決して成り立たない条件になっていました。
外側の列は、テーブル名を付けて書くように直しました。
// 外側のコンテナの id は、テーブル名を付けて書く
const containerId = sql`${containers}.${sql.identifier(containers.id.name)}`;
型チェックも通り、エラーも出ず、ただ数が 0 になる。画面を見ても「まだ何も入っていないコンテナ」に見えるだけなので、テストがなければ気づきにくい不具合です。#18でキャンプ一覧の進み具合を数えたときは、たまたま join のある問い合わせだったので、踏んでいませんでした。CLAUDE.md に注意を書き足しています。
できあがり

実装後のコンテナ一覧・プラン一覧・消耗品一覧(スマホの幅)
おわりに
- 見た目の変更は、Issue の前にサンプル画像を出してもらう。案が並べば、「事務的な感じ」のようなあいまいなメモからでも選べる
- サンプルを作る段階で、仕様の穴(緑の背景の上のチェックボックス)が見つかる
- 同じ名前のラベルを2つの画面に付けると、画面をまたぐテストが取り違える
- Drizzle のサブクエリから外側の列を指すときは、テーブル名を付ける
3つの Issue で、あとから見た目の直しが出たのは、タブの大きさ(使ってみて、目立ちすぎると分かった)だけでした。サンプルを作る手間は、十分に元が取れています。
次回は、カード表示を作り直した話です。こちらは、サンプルを見てもなお、受入のあとに直しが出ました。