TL;DR
- ロゴは出さない:オンボーディングは姓名入力・種別選択・スペース名入力・完了メッセージ読み取りといった「一点集中タスク」の連続。ブランドロゴはどのステップでも視線を分散させるノイズになる
- 最低 2 秒は見せる:実処理が瞬時に終わっても、即遷移は「バグ?」と感じられる。2 秒は「メッセージを読み取って納得する」のに必要な時間
- ステップ詳細は出さない:「user_profile を作成中」「employee を準備中」のような技術的進行はユーザーの関心外。抽象化したタイトル 1 行で十分
- 円スピナーではなくプログレスバー:丸がくるくるしているだけだとハングと区別できない。バーが伸び続ければ「進行中」が明確で、満タンになる瞬間にゴール感も出る
背景:ローカルファーストは速すぎる
niyase はローカルファーストで設計されているため、スペース作成は「ローカル DB にプロフィール・スペース・従業員のデータを書く」だけの瞬時処理です。デスクトップ(PGlite)こそマスタデータの初期投入で 2-3 秒かかりますが、モバイル(SQLite)では数十ミリ秒で全部終わります。
最初の実装はシンプルに await createWorkspace() → router.replace("/portal") でしたが、これを実機で動かすと「スペースを作成するボタンを押した瞬間にポータルが開く」体験になりました。
開発者から見れば「動いてる!速い!」ですが、ユーザーから見ると:
- 「あれ、押した直後にもう完了したの?」
- 「本当に作成された?それともエラー?」
- 「あれ、よく分からないけど画面が変わった」
完了が速すぎることは、それ自体が不安を生みます。
試行錯誤の過程
最初に試したのは「ステップ別の進捗チェックリスト」でした。Slack の onboarding を参考に、以下のような表示を入れました:
✓ ユーザー情報
✓ スペース
✓ 事業者情報
✓ メンバー情報
○ スペースを開く
これで「処理している感」は出るだろうと考えました。
しかし、実機で確認したところ、最初のチェックが付いた瞬間に遷移してしまい、バグのように見えました。
これには 2 つの問題が含まれていました:
- 進捗ステップを見ている前提でユーザーが画面を凝視しているのに、各ステップが数十ミリ秒で終わるので、最初のチェックマークが付いた直後に画面が変わってしまう
- そもそも「user_profile 作成中」「employee 準備中」のような単語はユーザーの語彙ではない。技術者にとっては意味のある進行ステップでも、エンドユーザーには「読み取る前に流れていく文字情報」でしかない
つまり、ステップ詳細表示は「動いている感」を出す目的では機能していたものの、ユーザーの認知負荷を増やしただけだったわけです。
4 つの判断
判断 1: ロゴは出さない
最初は「ブランド体験の一貫性」のために、オンボーディング全画面で [アイコン] niyase のロゴを上部に固定表示していました。
しかし、ロゴが常時居座ると集中を妨げると判断し、非表示にしました。
振り返ってみると、オンボーディングは 「ユーザーが一点に集中すべき重要な操作の連続」 です:
- 姓名を正しく入力する
- 法人 / 個人事業主 / 個人を選ぶ
- スペース名(slug)を考えて入力する
- 「スペースを作成しています」のメッセージを読み取って状況を理解する
どのステップも「やるべきこと・読み取るべきもの」が画面に 1 つだけあります。そこに [アイコン] niyase が居座ると視線が分散して認知負荷が増えます。Desktop ならウィンドウタイトルバーに、Web ならブラウザタブに既に niyase の名前が出ているので、ロゴは重複表示でもあります。
特に creating(スペース作成中)の画面は わずか 2 秒の表示時間 で「スペースを作成しています」というメッセージを読み取って納得してもらう必要があります。2 秒は短く、その間にユーザーが読み取る文字は最小限であるべきです。ロゴと「niyase」テキストが視界にあると、メッセージの読み取りと競合してノイズになります。
niyase の最終的なルール:オンボーディング全ステップでロゴは非表示。ブランド提示は header(ブラウザタブ / ウィンドウタイトル / アプリアイコン)に任せ、画面内ではユーザーの集中対象を 1 つに絞る。3 プラットフォーム(cloud / desktop / mobile)でこのルールを揃えました。
判断 2: 最低 2 秒は見せる
「短すぎてバグに見える」問題への対処として、ローディング画面の最低表示時間 2 秒 を導入しました。
Doherty Threshold(システム応答が 400ms 未満ならユーザーは「即座」と感じる)や Nielsen の応答時間ガイドライン(1 秒以内は思考の流れを保てる、それを超えると「待った」を意識)から、2 秒という数値には理屈があります:
- 1 秒未満:処理が速いことが価値
- 1〜2 秒:「処理しています」の認知に必要な時間
- 2〜3 秒:「準備完了」の納得感を得る時間
- 3 秒以上:「待たされる」フラストレーション
オンボーディングは一度しかやらない操作なので、2 秒程度の演出は許容範囲です。逆に、毎回繰り返す操作(CRUD 等)でこのレベルの遅延を入れると典型的なアンチパターンになります。
実装としては「実処理が 2 秒で終わるよう待つ」のではなく、「実処理は背景で並行に進めつつ、UI 表示を 2 秒は維持する」設計にしました:
const handleCreateStartedAt = Date.now();
// 実処理(瞬時に終わる)
await createWorkspace();
await createEmployee();
// ...
// 最低総時間保証
const MIN_TOTAL_MS = 2000;
const elapsed = Date.now() - handleCreateStartedAt;
if (elapsed < MIN_TOTAL_MS) {
await new Promise((r) => setTimeout(r, MIN_TOTAL_MS - elapsed));
}
router.replace("/portal");
判断 3: ステップ詳細は出さない
進捗チェックリスト型の表示はやめ、「スペースを作成しています」+ プログレスバーだけの構成にしました。
ユーザーが知りたいのは「今、何が起きているか」ではなく、「自分は何をすればいいか」と「いつ終わるか」の 2 つです。「user_profile を作成中」のような技術的進行は、どちらにも答えていません。
抽象化したタイトル 1 行で「スペースを作成しています」と伝え、進捗バーで「あとどのくらいで終わるか」を示す。これで必要な情報は揃います。
判断 4: 円スピナーではなくプログレスバー
最後の判断は、ローディングインジケータの種類です。
円形スピナー(くるくる丸)は、回り続けるだけで「進んでいる」も「止まっている」も区別がつきません。ハングしているのか動いているのか視覚的に判定不可能です。
一方、プログレスバーは:
- 「進んでいる」が明確:バーが伸びる動きはゴールに近づいている直感的フィードバック
- ゴール感:バーが満タンになる瞬間 = 完了、という視覚的なクライマックス
- ハング検知:バーが止まったら「何か変」と気づける
問題は「進捗率をどう計算するか」ですが、オンボーディングの場合は時間ベースの fake progress で十分です:
const progressInterval = setInterval(() => {
const ratio = Math.min((Date.now() - startedAt) / 2000, 1);
setProgress(ratio * 0.95); // 0 → 0.95 を線形に
if (ratio >= 1) clearInterval(progressInterval);
}, 50);
// 実処理完了時
clearInterval(progressInterval);
setProgress(1); // 95% → 100% にジャンプして「完了」を演出
fake progress は本来「不信感を招く」アンチパターンですが、それは長時間処理で本物の進捗が分からない場面の話です。2 秒で確実に完了するセットアップでは、ユーザーが偽進捗に気づく前に処理が終わるので不信感は生まれません。
3 プラットフォーム共通の UX
cloud(Web)/ desktop(Electron + PGlite)/ mobile(Expo + SQLite)の 3 つで、同じ UI 構成に揃えました:
- タイトル「スペースを作成しています」
- 線形プログレスバー(2 秒で 0→95%、完了で 100%)
- ロゴ・装飾なし
ただし、進捗の計算方法は環境差があります:
| プラットフォーム | 進捗の計算 |
|---|---|
| Desktop | 時間ベース fake progress(PGlite + seed で自然に 2-3 秒) |
| Mobile | 時間ベース fake progress(SQLite で実処理は瞬時) |
| Cloud | API ポーリングで実進捗(readyCount/totalCount)+ 時間ベース fallback |
Cloud はリモートで provisioning が走るので本物の進捗が取れますが、複数スペースを作る場合などで「次のポーリングまでバーが止まる」のを避けるため、時間ベースの fake progress も併用しています。
まとめ
オンボーディング UI を磨く過程で出した 4 つの判断は、どれも「ユーザーが今やるべきことに集中できるようにする」という原理に貫かれています:
- ロゴを外すのは「装飾より集中」
- 2 秒見せるのは「不安より納得」
- ステップ詳細を出さないのは「技術視点よりユーザー視点」
- バーを使うのは「ハングとの区別」
どれも単独では小さな判断ですが、組み合わさることで「何が起きたか分からない速さ」から「安心して結果を待てる短い時間」に変わります。
設計書や規約に「ローディングは 2 秒」と書いて終わりではなく、なぜその数字なのか、なぜ装飾を排するのかを共有することで、未来の自分や他の開発者が同じ判断を再現できるようになる ― それが UI の細部を磨く価値だと思います。