ハコヅミを作る #38:初めての人に、何から始めるかを案内する

前回(#37)は、ログインしていない人に見せる、紹介のページでした。今回は、ログインした直後の話です。公開までに要るものの、4つ目になります。

  • トップページに、「はじめに」のカードを出す
  • データが空の一覧に、説明と登録のボタンを足す
  • 先に要るものがないときは、そちらへ案内する

自分では、気づけなかった

ハコヅミは、次の順に登録しないと、持ち物チェックまでたどり着けません。

  1. ギアを登録する
  2. プランを作る(持っていくギアの組み合わせ)
  3. キャンプを登録する(プランを選ぶと、チェックのリストができる)

プランがないと、キャンプは登録できません。ところが、この順番は、画面のどこにも書かれていませんでした。

自分は、Notion から 56 件のギアを取り込んで、使い始めています(#7)。空の状態から始めたことが、一度もありません。公開すると、全員が、空の状態から始まります。

初めてログインした人に見えるのは、メニューと、「まだギアが登録されていません。」の一文だけでした。


「はじめに」のカード

トップページに、手順を並べたカードを出すことにしました。

今回も、画面イメージを2つ出してもらっています。

  • 案A:4つの手順を、説明つきで縦に並べる
  • 案B:手順は名前だけを小さく並べ、今やる手順だけを大きく出す
トップページの「はじめに」のカード(案A)が2つ並んでいる。左は何も登録していないときで、4つの手順のうち、1つ目の「ギアを登録する」に色が付き、「ギアを登録」のボタンがある。右はギアを登録したあとで、1つ目にチェックの印と「済み」が付き、3つ目の「プランを作る」に色とボタンが移っている

案A。左:何も登録していないとき。右:ギアを1件登録したあと

トップページの「はじめに」のカード(案B)が2つ並んでいる。手順の名前が小さく並び、その下に、今やる手順だけが大きく出ていて、登録のボタンがある

案B。縦には短いが、ほかの手順の説明は見えない

案B のほうが、縦に短くて済みます。それでも、案A を選びました。

案A は、全部の手順の説明が見えます。「コンテナは、やらなくても先へ進める」「プランとは、持っていくギアの組み合わせ」が、押す前に分かります。初めての人がこのカードを読むのは、最初の数回だけです。縦に長いことの害は、小さいと考えました。

コンテナは、飛ばせる手順

手順は4つありますが、コンテナは、やってもやらなくてもかまいません。コンテナを使わなくても、持ち物チェックはできます。

そこで、コンテナは「今やる手順」にならないようにしました。ギアを登録すると、色とボタンは、コンテナを飛ばして、プランに移ります。コンテナには、小さな登録のリンクだけを出しています。

キャンプを登録したら、出さない

カードは、キャンプを1件登録すると、出なくなります。手順を一通り終えた、ということだからです。


新しいテーブルを作らない

こういう案内を作るとき、「案内を見終えた」「案内を閉じた」という記録を、利用者ごとに持つやり方があります。閉じるボタンを付けるなら、その記録が要ります。

今回は、記録を持たないことにしました。出すかどうかを、今の登録数だけで決めます。

  • ギアが1件以上ある → 「ギアを登録する」は済み
  • キャンプが1件以上ある → カードを出さない

登録数は、上限の仕組み(#29)のために、もう数えています。同じ数を、そのまま使えました。テーブルも、マイグレーションも、増えていません。

代わりに、できないこともあります。カードを、手で閉じることはできません。キャンプを登録するまで、出続けます。Issue の「対象外」に、理由と一緒に書いておきました。

判定は、短い関数に切り出して、ユニットテストしています。「コンテナだけがあって、ギアがない」「ギアを全部消したが、プランは残っている」といった、画面からは作りにくい場合も、関数なら、すぐに試せます。

export function gettingStartedSteps(counts: StartCounts): StartStep[] | null {
	if (counts.camps > 0) return null;
	const currentKey = STEP_KEYS.find((key) => key !== 'containers' && counts[key] === 0);
	return STEP_KEYS.map((key) => ({
		key,
		done: counts[key] > 0,
		optional: key === 'containers',
		current: key === currentKey
	}));
}

空の一覧に、説明とボタンを足す

一覧のほうも直しました。#31で、「まだ〜が登録されていません。」の表示を、5つの一覧でそろえてあります。そこに、そのものの説明と、登録のボタンを足しました。

4つの画面が並んでいる。ギアの一覧には説明と「ギアを登録」のボタン。プランの一覧には「先にギアを登録してください。」と「ギアを登録」のボタン。キャンプの一覧には「先にプランを作ってください。」と「プランを登録」のボタン。プランの登録の画面には、入力欄の上に「先にギアを登録してください。」のカードがある

左から、ギアの一覧、プランの一覧、キャンプの一覧、プランの登録の画面(どれも、何も登録していないとき)

「コンテナ」や「プラン」は、ハコヅミの中の言葉です。初めての人には、何のことか分かりません。空の一覧は、その言葉を説明する、ちょうどよい場所でした。

  • コンテナ:「ギアをまとめて入れる箱です。箱を積めば、中のギアは全部積んだことになります。」
  • プラン:「ソロ・ファミリーなど、持っていくギアの組み合わせです。」

先に要るものへ、案内する

プランの一覧を、ギアが1件もない状態で開いたとします。ここで「プランを登録」のボタンを出しても、入れるギアがありません。

先に要るものがないときは、そちらへ案内することにしました。

  • ギアがない → プランの一覧と登録の画面に、「先にギアを登録してください。」
  • プランがない → キャンプの一覧と登録の画面に、「先にプランを作ってください。」

2つの登録の画面は、扱いを変えています。キャンプは、プランがないと登録できないので、案内だけを出します。プランは、消耗品だけのプランも作れるので、入力欄を出したまま、上に案内を出しました。


置き場所を間違えて、ヘッダーの上に出た

最初に撮った画面イメージは、おかしなことになっていました。「はじめに」のカードが、ヘッダーよりも上に出ています。

原因は、単純でした。カードを置く場所を、ページの中身ではなく、ページのタイトルなどを書くところ(<svelte:head>)に入れていたのです。Claude Code が、文字を置き換えるときに、同じ書き出しの行を取り違えていました。

型チェックは、通っていました。文法としては、間違っていないからです。画面を撮って、見て、初めて分かりました。

もう1つ、同じ流れで起きたことがあります。直したあとに撮り直したはずの画像が、前と同じでした。直し方に誤りがあって、サーバーが起動していなかったのです。画像のファイルは、前に撮ったものが、そのまま残っていました。

「撮り直した」と「画像がある」は、同じではありません。撮ったあとに、画像を1枚ずつ見る。ブログの画像で決めた習慣(#18)が、ここでも役に立ちました。


おわりに

  • 自分が通らなかった道は、自分では気づけない。空の状態は、わざと作って見る
  • 初めての人が読む案内は、短さより、分かることを取る
  • 案内を出すかどうかは、今あるデータから決められないかを、先に考える。記録を持つと、テーブルも、消す手間も増える
  • できなくなること(手で閉じる)は、理由と一緒に書いておく
  • 空の一覧は、そのサービスの言葉を説明する場所になる
  • 型チェックが通っても、画面が正しいとはかぎらない。撮って、見る

これで、ベータ版を始める前に要るものが、すべて本番に出ました。次回は、いよいよ、本番を守ってきた Cloudflare Access を外します。

← トップページに戻る