SOUZOHSPECS

一般向けアプリ ・ Souzoh Delivery

デリバリー

注文から到着まで、配達が進む様子がリアルタイムに見える

デモを触る ↗MD

本番で使いたい方へデータの保存・セキュリティ・運用ルール・公開は、このデモには入っていません
  1. 要件定義書を保存する

    パソコンに delivery-app のようなフォルダを作り、その中に保存します。

    要件定義書.md として保存

  2. ChatGPT で画面イメージを作る デザインにこだわりたい人

    ChatGPT に要件定義書を添付して、下の文を送ります。雰囲気のところは、好きな言葉に書きかえてください。

    ChatGPT に送る
    添付の要件定義書は、これから作るアプリ「デリバリー」のものです。
    このアプリのいちばん大事な画面を、デザイン画像にしてください。
    実際のアプリのように、文字やボタンまで入れてください。
    雰囲気:(例:やさしい/高級感/ポップ/シンプル など、好きな言葉で)

    気に入るまで「もっと明るく」「ボタンを大きく」のように直してもらい、できた画像を保存します。ほかの画面も欲しければ、同じように作ってもらえます。

  3. Claude Code に渡して「作って」と頼む

    Claude のデスクトップアプリで「Code」タブを開き、「Select folder」から delivery-app を選びます。画面イメージがある人は、入力欄に画像をドラッグして貼りつけてから、下の文を送ります。詳しく

    Claude Code に送る(画像あり)
    @要件定義書.md をもとに、このアプリを作ってください。
    デザインは添付の画像に合わせてください。
    「母艦」など手元に無いものは、一から作ってください。
    Claude Code に送る(画像なし)
    @要件定義書.md をもとに、このアプリを作ってください。
    「母艦」など手元に無いものは、一から作ってください。

    作り終わるまで、しばらくかかります。途中で「許可しますか」と聞かれたら、内容を見て許可してください。

  4. 動かして確かめる・直す

    できたら、実際に画面を開いて触ります。直してほしいところは、スクリーンショットを貼りつけて、言葉で伝えれば大丈夫です。

    Claude Code に送る
    アプリを起動して、ブラウザで開けるようにしてください。
    Claude Code に送る
    (画面名)で(操作)をすると(今の動き)になります。
    (こうなってほしい動き)にしてください。
    Claude Code に送る
    このエラーを直してください。
    
    (ここにエラーの文をそのまま貼る)
  5. 途中で止まったら 必要なときだけ

    要件定義書が長いので、一度で作りきれないことがあります。そのときは新しい会話で、こう送ります。

    Claude Code に送る
    @要件定義書.md の続きを作ってください。
    まず、どこまでできているかを確かめてから進めてください。

    それでも進まないときは、30分無料相談でお手伝いします。公式LINEからのご相談もどうぞ。

    1段階ずつ頼みたいとき(全6段階)

    要件定義書には、作る順番が Session 0〜5 で書かれています。止まりやすいときは、1つずつ頼むと確実です。

    Session 0母艦を複製 → Portal/Marketから fetch-images.mjs SmartImage Price Carousel motion.js をコピー → theme.css app.config.js data/ config/demo.js を差し替え → npm run fetch:images で62枚取得 → shell/ にバッジ対応を追加 → ビルドを通す
    Claude Code に送る
    @要件定義書.md の「実装手順」にある Session 0 を作ってください。
    Session 1ui/ の4点(StepBar / LiveBand / QtyStepper / SwipeRow)+ motion.js にG〜N追記 + store.js のカート/注文の状態設計
    Claude Code に送る
    @要件定義書.md の「実装手順」にある Session 1 を作ってください。
    Session 2D1店舗一覧 + D2店舗詳細
    Claude Code に送る
    @要件定義書.md の「実装手順」にある Session 2 を作ってください。
    Session 3D3カスタマイズ + D4カート + D5注文確定(金額計算をここで完成させる)
    Claude Code に送る
    @要件定義書.md の「実装手順」にある Session 3 を作ってください。
    Session 4D6配達追跡(抽象マップ・ルート・タイマー・§7.3のシーケンス)
    Claude Code に送る
    @要件定義書.md の「実装手順」にある Session 4 を作ってください。
    Session 5D7履歴 + D8配達員モード + D9検索 + 仕上げ
    Claude Code に送る
    @要件定義書.md の「実装手順」にある Session 5 を作ってください。
  6. 本番で使うなら

    できたアプリはデモです。データの保存・セキュリティ・運用のルール・公開の仕方は入っていないので、そのまま公開すると誰でも中身を見られます。本番で使うのに何が要るかは、このページの下にまとめました。

    自社向けに作り変えるなら、まずこう聞いてみてください。

    Claude Code に送る
    この要件定義書は、記事に埋め込むデモ用に書かれています。
    自分の会社で実際に使えるように変えたいです。外したほうがいいところと、残したほうがいいところを教えてください。

    本番運用について相談する

準備や、うまくいかないときの対処は 作り方ガイド にまとめています。分からないときは 30分無料相談 へどうぞ。

これはデモ用の要件定義書です。本番で使うには、あと4つ必要です

MDでダウンロード

デリバリー・配送アプリ テンプレート 要件定義書

プロダクト名:Souzoh Delivery

シリーズ6本目。シリーズ中で最も「状態」を持つ1本です(カート・注文・配達進行が同時に動く)。 4条件(デザイン/画像自動取得/動き/第一印象)は本書でも必達要件として引き継ぎます。


1. 目的とゴール

項目 内容
コンセプト 近隣の店から料理・商品を注文し、配達が進んでいく様子がリアルタイムに見えるデリバリーアプリ
一次目的 「注文して、追跡する」という一連の流れをデモで通し切る
二次目的 配達員視点(§9 D8)も用意し、配送業・多店舗事業者の読者に自社の配車/配送管理を想起させる
成功条件 読者が1品カートに入れて注文し、配達員が地図上を動くところまで到達する
非ゴール 実決済・実配車・実地図・店舗管理画面

この題材の難所:前5本は「見る」が中心でしたが、本作はカート(複数商品+オプション+数量)配達の時間進行という2つの状態を同時に持ちます。§13でセッションを5分割し、カートを独立させているのはこのためです。


2. 引き継ぐ4条件

条件 対応章 本作での実現方法
① デザインにこだわる §5 交通サイネージ路線。極太コンデンス書体+横帯構成+蛍光オレンジ1色
② ネットから画像を自動取得 §6 Portalの fetch-images.mjs を流用。本作は62枚(料理写真が主役)
③ 動きをつける §7 Portalの motion.js を継承 + 本作固有の8種。うち3種が配達追跡
④ 一目ですごい §8 常駐ライブバンドルートリボンの2点に集中投下

3. スコープ

3.1 作るもの

# 画面 優先度 既存資産との関係
D1 店舗一覧(ホーム) A 母艦S1/Market K1を改修
D2 店舗詳細(メニュー) A Market K2を改修
D3 商品カスタマイズ S 新規。オプション・数量・小計
D4 カート S 新規。本作最大の状態管理
D5 注文確定(配達先・時間・支払い) A Market の購入モーダルを拡張
D6 配達追跡 S 新規。本作の本体
D7 注文履歴 B Market K6を改修
D8 配達員モード B 新規。1画面のみ(§9)
D9 検索・絞り込み B Market K3/Reviews R4を流用

3.2 作らないもの

  • ❌ バックエンド・DB・認証・データ永続化
  • 実地図(タイル配信)。§3.3で代替案を規定
  • ❌ 実決済・クーポン計算・ポイント
  • ❌ 店舗側の受注管理画面
  • ❌ 配達員の実位置取得(GPS)
  • ❌ ダークモード、多言語、テストコード

3.3 地図の扱い ★重要な設計判断

Reviewsで地図を外した理由(外部依存・商用条件・見せ場にならない)は本作でも有効ですが、デリバリーだけは「配達位置が見える」ことが体験の核なので、単純に外すわけにいきません。

そこで手描きSVGの抽象マップを採用します。

実地図タイル                        抽象SVGマップ(採用)
─────────────────────────────────────────────────
外部タイル配信に依存               依存ゼロ。ファイル1枚
商用利用条件が提供者ごとに違う     判断が発生しない
スクショが灰色で地味               配色を自分で決められる
ルート描画はライブラリ経由         SVGパスなので完全に制御できる
実装:ライブラリ+API調整          実装:静的SVG+path animation

内容:街区・幹線道路・川・公園を、--color-line の細線と --color-surface-2 の面だけで抽象化した1枚のSVG(約900×600)。実在の地名は入れず、さくら通り 中央公園 のような架空表記にします。その上に注文の配達ルートを stroke-dasharray で描き、配達員のドットが offset-path で移動します。

これは妥協ではなく上位互換です。 グレーの地図タイルより、自分で配色した抽象マップのほうが記事のスクショとして明確に強い。実運用で本物の地図に差し替えたい場合の差し替え点は features/tracking/RouteMap.jsx の1ファイルだけになるよう、コンポーネント境界を切ってください。


4. 技術構成

Vite + React 18(JSX)
Tailwind CSS v4
lucide-react
motion(motion.dev)… §7.4で使用範囲を限定
────────── 既存資産から持ち込む ──────────
scripts/fetch-images.mjs        (Portal)
src/components/ui/SmartImage.jsx(Portal)
src/motion.js                   (Portal)
src/components/ui/Price.jsx     (Market)★本作で本領発揮
src/components/ui/Carousel.jsx  (Market)
src/components/ui/Stepper.jsx   (Market)★横向きに拡張
────────── 追加ライブラリなし ──────────

Price.jsx がここで効きます。 本作は商品価格・オプション加算・小計・配送料・手数料・合計と、金額の表示箇所が6種類あります。Marketで作った統一コンポーネントがなければ、ここで表記ゆれが必ず出ていました。


5. デザイン仕様 ★条件①

5.1 方向性

シリーズ6本の住み分けです。

アプリ 基調 書体 質感
SNS 明るいウォームグレー サンセリフ 余白
Match ディーププラム サンセリフ 舞台
Market サンセリフ 陳列
Portal ディープインク サンセリフ ガラス/管制室
Reviews クリーム セリフ 雑誌
Delivery ニュートラルグレー 極太コンデンス 交通サイネージ/物流

狙うのは空港の発着案内板や配送伝票の質感です。デリバリーの本質は「時間」と「現在地」なので、駅や空港の案内サインが持つ即読性を設計言語にします。

具体的には——

  • 横帯(バンド)構成。カードを並べるのではなく、画面を横方向の帯で切る
  • 極太コンデンス書体の大きな数字(18分 ¥2,480
  • 蛍光オレンジは進行中の状態にのみ使う。それ以外は無彩色
  • 角丸は極小(6px)。サインボードの硬さ

5.2 シグネチャー①:常駐ライブバンド

注文が進行中の間、画面下部(タブバーの直上)に横帯が常駐します。

┌────────────────────────────────────────────┐
│ ● 調理中                    あと 18分  ›   │  ← 蛍光オレンジの帯
└────────────────────────────────────────────┘
  • どの画面にいても消えない。タップでD6配達追跡へ(ボトムシートで迫り上がる)
  • 左の は状態色で明滅(#11パルス)
  • 残り時間は分単位で実際に減っていく(デモでは加速。§7.3)
  • 状態が変わるたびに帯のテキストが上方向にスライドで入れ替わる(#G)

これが本作で最も"アプリらしい"要素です。 実装は固定配置の1コンポーネントですが、画面を移動しても注文が生き続けている感覚が出る。スクショにも必ず写るので、どのカットを撮っても「動いているアプリ」に見えます。

5.3 シグネチャー②:ルートリボン

D6の抽象マップ上を配達員のドットが移動します(§3.3・§7.2の#I)。

  • ルートは --color-accent の3px実線。通過済みは実線、未通過は破線
  • 配達員ドットは12pxの円+外側に薄いパルス。offset-path: path(...) で移動
  • 店舗ピンと配達先ピンは異なる形状(■と▼)で、色に頼らず区別
  • 到着が近づくと(残り3分)、配達先ピンが1.6秒周期で拡大縮小

5.4 デザイントークン(src/theme.css

@theme {
  /* ---- Base ---- */
  --color-bg:        #F1F1EF;  /* ニュートラルグレー。紙でも白でもない */
  --color-surface:   #FFFFFF;
  --color-surface-2: #E5E5E2;  /* 帯・入力欄・マップの面 */
  --color-ink:       #121212;  /* 純黒に近い。サイネージの黒 */
  --color-ink-sub:   #6A6A66;
  --color-ink-inv:   #FFFFFF;
  --color-line:      #D6D6D2;

  /* ---- Accent(進行中にのみ使う) ---- */
  --color-accent:    #FF5A00;  /* 蛍光オレンジ */
  --color-accent-2:  #0B57D0;  /* 青:完了・確定済み */
  --color-ok:        #1B7F3B;
  --color-warn:      #B54708;  /* 遅延 */

  /* ---- 状態色(配達ステップ) ---- */
  --color-step-received: #6A6A66;  /* 注文受付 */
  --color-step-cooking:  #FF5A00;  /* 調理中 */
  --color-step-pickup:   #FF5A00;  /* 受け取り */
  --color-step-moving:   #FF5A00;  /* 配達中 */
  --color-step-done:     #1B7F3B;  /* 到着 */

  /* ---- Type ---- */
  --font-display: "Archivo Expanded", "Anton", "Zen Kaku Gothic New", sans-serif; /* 700/800 */
  --font-body:    "Noto Sans JP", sans-serif;                                     /* 400/500 */
  --font-num:     "Roboto Condensed", sans-serif;  /* ★極太コンデンス。時間・金額 */

  --text-eta:    clamp(44px, 6vw, 76px)/0.95;  /* 「18分」の巨大表示 */
  --text-hero:   clamp(26px, 3.2vw, 38px)/1.15;
  --text-title:  18px/1.4;
  --text-body:   14px/1.7;
  --text-label:  11px/1.2;   /* 大文字+letter-spacing .14em */

  /* ---- Space ---- */
  --space-1: 4px;  --space-2: 8px;  --space-3: 12px;
  --space-4: 16px; --space-6: 24px; --space-8: 32px;

  /* ---- Shape ---- */
  --radius-card:  6px;    /* シリーズ最小。サインボードの硬さ */
  --radius-input: 6px;
  --radius-pill:  999px;
  --shadow-card:  none;
  --shadow-band:  0 -8px 24px rgba(18,18,18,.10);  /* 常駐バンドのみ */
  --shadow-pop:   0 16px 48px rgba(18,18,18,.20);

  /* ---- Motion(Portalから継承) ---- */
  --ease-out:   cubic-bezier(.16,1,.3,1);
  --ease-inout: cubic-bezier(.65,0,.35,1);
  --dur-fast:   140ms;
  --dur-base:   240ms;
  --dur-slow:   520ms;
}

5.5 タイポグラフィの決め事

  • 時間と金額は必ず --font-num(極太コンデンス)+ tabular-nums。ETAの 18分 が巨大なコンデンス数字で出るだけで、このアプリの顔になります
  • ラベル(配達先 お届け時間 など)は --text-labelletter-spacing: .14em--color-ink-sub
  • 見出しは --font-display(エクスパンデッド/コンデンスの太字)。本文には使わない
  • 状態名(調理中 配達中)は必ず大きめの太字。小さい灰色文字にしないこと

5.6 やらないこと(NGリスト)

  • ❌ オレンジを装飾に使う(進行中の状態にのみ。ボタンの色ですら、進行に関係なければ黒)
  • ❌ 料理写真に強い角丸(--radius-card 6pxを超えない)
  • ❌ カードへの影(常駐バンドのみ --shadow-band
  • ❌ 状態を色だけで区別する(必ずアイコン形状かラベルを併記。§5.3のピン形状もこの原則)
  • ❌ 角丸の混在(3種のみ)
  • ❌ 「🍕」等の絵文字をカテゴリアイコンに使う(lucide-reactに統一)

6. 画像の自動取得 ★条件②

6.1 方針

Portalの scripts/fetch-images.mjs無変更でコピーimageManifest.js のみ差し替えます。表示側も SmartImage.jsx の3層フォールバックをそのまま使用。仕様はPortal §6.3と同一のため本書では繰り返しません。

6.2 枚数:62枚

シリーズ最多です。 デリバリーは料理写真がそのまま商品価値なので、ここは削れません。

用途 枚数
店舗カバー 10
メニュー商品写真 40(1店舗あたり4品)
カテゴリバナー 6
特集・キャンペーン 3
配達員アバター 0(§6.3の通り生成
空状態・完了画面のイラスト代替 3
合計 62

並列4本で40秒程度です。Session 0で必ず取得を完了させてください(§13)。

6.3 配達員・ユーザーのアバター

Portal/Reviewsと同じ判断で、実在人物の写真は使いません。ID由来グラデーション+頭文字です。

本作では配達員の顔が追跡画面に出るため特に重要です。架空の配達員に実在の人物の顔を充てた画面を、テンプレートとして配布すべきではありません。

6.4 マニフェスト抜粋

export default [
  // 店舗カバー(10)
  { seed:"st-kamameshi", w:1200, h:800, use:"釜飯 とよ" },
  { seed:"st-thai-lek",  w:1200, h:800, use:"タイ食堂 レック" },
  { seed:"st-burger-9",  w:1200, h:800, use:"NINE BURGER" },
  { seed:"st-soba-koke", w:1200, h:800, use:"手打ちそば 苔" },
  { seed:"st-curry-tomo",w:1200, h:800, use:"カリー トモ" },
  { seed:"st-deli-mori", w:1200, h:800, use:"デリカ もり" },
  { seed:"st-pizza-fumo",w:1200, h:800, use:"PIZZA FUMO" },
  { seed:"st-cafe-yuru", w:1200, h:800, use:"ゆるり珈琲" },
  { seed:"st-sweets-ao", w:1200, h:800, use:"洋菓子 あお" },
  { seed:"st-mart-24",   w:1200, h:800, use:"まるとくマート" },

  // メニュー商品(40)… {店舗seed}-m1 〜 -m4、すべて 800×800(正方形)
  // カテゴリバナー(6)… cat-washoku / cat-asia / cat-burger / cat-cafe / cat-sweets / cat-mart(900×400)
  // 特集(3)……… feature-fast / feature-solo / feature-late(1400×500)
  // 空状態(3)…… empty-cart / empty-search / done-thanks(800×600)
];

7. 動き ★条件③

7.1 Portalからの継承

# 名称 本作での適用先
1 段差入場 D1の店舗カード(50ms差)
2 カウントアップ 合計金額・ETA
4 スクロール連動リビール D2のメニュー、D1下部
6 View Transitions 画面遷移 全ページ
7 共有要素モーフ 店舗カード → D2、商品 → D3
9 リスト入れ替え D9絞り込み結果、D4カート内
11 ライブパルス 常駐バンドの 、配達員ドット
14 トースト 全体

7.2 本作固有の動き(8種)

# 名称 対象 仕様 reduced時
G バンドのテキスト入れ替え 常駐バンド 状態変化時、旧テキストが上へ抜け新テキストが下から入る。各220ms、--ease-out 即差し替え
H カート投入の飛翔 D3 → カート 商品サムネがカートアイコンへ弧を描いて縮小移動(480ms)+着地時にバッジが1.25倍→戻る バッジ数のみ変化
I 配達員の移動 D6ルートリボン offset-path に沿ってドットが進行。ルートの実線部分が同期して伸びる 現在地に静止表示
J ステップ帯の進行 D6上部 横向きStepper。次ステップへ移る際、帯が左から右へ塗り替わる(600ms) 即塗り替え
K ETAカウントダウン D6中央 巨大コンデンス数字。分が変わる瞬間だけ上方向スライド(180ms) 数字のみ変化
L オプション選択の小計反映 D3 オプションを選ぶたびに小計が180msでカウント変化 即変化
M カート行のスワイプ削除 D4 左スワイプで削除ボタン露出(判定64px)。削除時は高さ0へ縮小(280ms) ボタン常時表示
N 到着セレブレーション D6完了時 配達先ピンから同心円が2回広がる(900ms)+「お届け完了」が拡大表示 テキストのみ

7.3 デモの時間圧縮 ★重要

実際の配達は30分かかりますが、デモは90秒で全ステップを通します

経過 状態 ETA表示
0秒 注文受付 32分
8秒 調理中 26分
30秒 店舗で受け取り 18分
45秒 配達中(ドット移動開始) 12分
80秒 まもなく到着(配達先ピン拡大) 3分
90秒 お届け完了(#N)
  • ETAはリアルタイムで減り続ける(表示は分単位、内部は秒管理)
  • D6を開いていなくても進行する(常駐バンドが更新される)
  • 進行速度は src/config/demo.js の1定数(DEMO_SPEED = 20)で制御。実時間にしたい場合は1にするだけ

★90秒という設定が要点です。記事の読者が「注文 → 到着」を1回通せる長さで、かつ待たされている感じがしない。長すぎると誰も最後まで見ません。

7.4 motion ライブラリの使用範囲(限定)

  • ✅ #7 共有要素モーフ
  • ✅ H カート投入の飛翔
  • ✅ M スワイプ削除の高さアニメーション
  • ✅ #9 リスト入れ替え
  • ❌ 上記以外はCSS / WAAPI / View Transitions(I・J・K・N はすべて自前offset-pathstroke-dashoffset で足ります)

8. 「一目ですごい」の設計 ★条件④

8.1 勝負するのはD6(配達追跡)

Portalはホーム、Reviewsは店舗詳細、本作は配達追跡です。ここが記事のサムネイルになります。

D6のファーストビューに以下が同時に載ります。

  1. 巨大なコンデンス数字のETA18分)が減り続けている
  2. 抽象マップ上を配達員のドットが動いている
  3. ルートの実線が伸びている
  4. 上部のステップ帯が塗り替わる
  5. 配達員名・車両種別・評価の小さな行
  6. 下部に注文内容のサマリー

「動いているスクリーンショット」を撮るのは不可能ですが、動いているアプリの静止画は伝わります。 ルートの実線/破線の切り替わり位置に配達員ドットがあり、ETAが半端な数字(18分)である。この2点だけで「今まさに動いている」ことが静止画から読み取れます。

8.2 D6のレイアウト(モバイル基準・PCは中央600px)

┌────────────────────────────────────────┐
│ ①受付 ─ ②調理 ─ ③受取 ─ ④配達 ─ ⑤到着 │ ← 横ステップ帯(J)
├────────────────────────────────────────┤
│                                        │
│        抽象マップ(SVG)                │
│         ■店 ━━━━●┈┈┈┈▼配達先          │ ← ルートリボン(I)
│                                        │
├────────────────────────────────────────┤
│  あと                                  │
│  18分            ← --text-eta(K)      │
│  12:40 - 12:50 に到着予定               │
├────────────────────────────────────────┤
│ ▨ 佐々木さんが配達中    自転車 / ★4.9  │
├────────────────────────────────────────┤
│ 釜飯 とよ ・ 3品 ・ ¥2,480             │
│ 注文内容を見る ›                        │
└────────────────────────────────────────┘

PCでは左にマップ、右にETA+配達員+注文内容の2カラム。

8.3 常駐ライブバンドの効き

§5.2の通り。どの画面のスクショにも必ず写るため、記事に載せる画像が全部「注文が進行中のアプリ」になります。1コンポーネントの実装で、全カットの印象が変わる投資です。


9. 画面仕様

D1. 店舗一覧(ホーム)

  • 上部:配達先の表示(蔵前2丁目 ›。タップでトースト「デモでは変更できません」)+ 検索
  • カテゴリバナー横スクロール(6枚)
  • フィルタチップ:配達が速い 送料無料 新着 評価4.5以上
  • 特集バナー3枚
  • 店舗カード(横帯レイアウト):
    • 左に正方形のカバー写真(96px)、右に店名・ジャンル・18-28分送料 ¥150★4.6(212)
    • 配達時間を --font-num で強調。デリバリーで最も見られる数字はここ
    • 営業時間外は写真を grayscale(1) opacity(.5)準備中

D2. 店舗詳細(メニュー)

  • カバー写真(高さ200px)+ 下端グラデーション
  • 店名・ジャンル・18-28分送料 ¥150最低注文 ¥800★4.6
  • カテゴリのスティッキータブ(人気 ごはん サイド ドリンク)。スクロール連動で現在地がハイライト
  • メニュー行:左に商品名・説明1行・価格、右に正方形写真(80px)
  • 品切れは --color-ink-sub に脱色 + 品切れ バッジ、タップ不可
  • 下部:カートに商品があれば カートを見る(3品・¥2,480) の黒帯

D3. 商品カスタマイズ ★固有画面

ボトムシート(PCは中央モーダル)。

# 要素 仕様
1 商品写真 1:1、上部に大きく
2 商品名・説明・基本価格
3 必須オプション ラジオ。例:サイズ(並 +¥0 / 大 +¥200 / 特盛 +¥380)。未選択では追加不可
4 任意オプション チェックボックス。例:トッピング(温玉 +¥80 / のり +¥50 / ねぎ増し +¥30)。最大3つまで
5 要望メモ 100字。プレースホルダ「アレルギーやご要望があればご記入ください」
6 数量 1 +。1〜20
7 追加ボタン カートに追加 ¥1,180選択に応じて金額がリアルタイム変化=動きL)

追加 → シート下降 + カート飛翔アニメーション(H) + トースト。

D4. カート ★固有画面・状態管理の本体

  • 店舗名(1店舗のみ。別店舗の商品を追加しようとすると確認ダイアログ「カートを空にして新しく注文しますか?」)
  • 商品行:写真・商品名・選択したオプション(大 / 温玉 / のり)・数量ステッパー・小計
  • 左スワイプで削除(動きM)。削除時は 元に戻す 付きトースト(5秒)
  • + 商品を追加 → D2へ戻る
  • 金額内訳(Price.jsx を使用):
商品小計                  ¥ 2,180
配送料                    ¥   150
サービス料 5%             ¥   109
────────────────────────────────
合計                      ¥ 2,439   ← --font-num / 大きく
  • 最低注文金額未達の場合:あと ¥320 で注文できます を表示し、確定ボタンを非活性
  • 空状態:empty-cart 画像 + お店をさがす

D5. 注文確定

1ページに縦積み(モーダルにしない。項目が多いため)。

# 項目 仕様
1 配達先 ダミー住所プリセット + 建物名・部屋番号 の入力欄(任意)
2 受け取り方法 2択:手渡し / 置き配。置き配を選ぶと 置き場所 の3択が出現
3 配達時間 2択:できるだけ早く(18-28分) / 時間を指定 → 30分刻みのセレクト
4 支払い方法 3択ラジオ(カード/QR決済/現金)
5 配達員へのメモ 100字
6 金額内訳 D4と同一表示

注文を確定する → 確定アニメーション → D6へ自動遷移 + 常駐バンド起動 + §7.3のタイマー開始

D6. 配達追跡 ★本体

§8.2の構成。加えて:

  • ステップ帯の各ノードをタップすると、その工程の説明がポップオーバーで出る
  • 配達員にメッセージ → 母艦S7のチャットを流用(送信1.2秒後に定型返信)
  • 到着後(#N):配達を評価する(星3択 + コメント任意)→ 送信でD7へ
  • 注文が無い状態でD6を開いた場合:直近の完了注文を表示 + もう一度注文する

D7. 注文履歴

  • タブ:進行中 / 完了
  • 行:店舗写真・店名・注文日時・品数・合計・状態バッジ
  • もう一度注文する → 同じ内容でカートに投入(オプションも復元
  • タップで注文詳細(明細+配達実績の時刻)

D8. 配達員モード ★1画面のみ

設けた理由:読者に配送業・多店舗運営が多いためです。「顧客側だけでなく配達側の画面も作れる」ことを1画面で示せば、相談の入口が広がります。あくまで1画面に留め、コストを膨らませません。

  • ヘッダーのアイコンから切替(お客様 ⇄ 配達員
  • 内容:
    • オンライン/オフラインのトグル(オンライン時のみ以下を表示)
    • 新着依頼カード:店舗名・受取先・配達先・距離 2.1km・報酬 ¥620受ける / 見送る
    • 受注すると、進行中タスクとして 店舗へ向かう → 受け取り完了 → 配達先へ → 配達完了 の4ボタン順送り
    • 本日の実績:完了 8件 走行 21.4km 報酬 ¥5,240(すべて --font-num
  • 顧客側D6の状態と連動させません(別データ。連動させると状態管理が跳ね上がります)

D9. 検索・絞り込み

Market K3/Reviews R4と同構造。ジャンル 配達時間(20分以内/30分以内) 送料 評価 営業中のみ


10. データモデル

// store
{ id:"st01", name:"釜飯 とよ", genre:"和食",
  cover:{ seed:"st-kamameshi", w:1200, h:800 },
  etaMin:18, etaMax:28, deliveryFee:150, minOrder:800,
  score:4.6, reviewCount:212,
  hours:{ open:"11:00", close:"21:30" }, open:true,
  categories:["人気","ごはん","サイド","ドリンク"] }

// menuItem
{ id:"mi01", storeId:"st01", category:"人気",
  name:"五目釜飯", desc:"季節の具材を土鍋で炊き上げます",
  price:980, image:{ seed:"st-kamameshi-m1" }, soldOut:false,
  optionGroups:[
    { id:"g1", label:"サイズ", required:true, max:1,
      options:[{id:"o1",label:"並",add:0},{id:"o2",label:"大",add:200},{id:"o3",label:"特盛",add:380}] },
    { id:"g2", label:"トッピング", required:false, max:3,
      options:[{id:"o4",label:"温玉",add:80},{id:"o5",label:"のり",add:50},{id:"o6",label:"ねぎ増し",add:30}] }
  ] }

// cartLine
{ lineId:"cl01", menuItemId:"mi01", qty:2,
  selected:{ g1:"o2", g2:["o4","o5"] }, note:"",
  unitPrice:1310 }          // 基本980 + 大200 + 温玉80 + のり50

// order
{ id:"od01", storeId:"st01", lines:[...],
  subtotal:2180, deliveryFee:150, serviceFee:109, total:2439,
  method:"手渡し"|"置き配", placedAt:"...",
  step:1..5, etaSec:1920,           // 秒管理。表示は分
  courier:{ name:"佐々木", vehicle:"自転車", score:4.9, avatarSeed:"c01" },
  route:"M 80 460 L 240 460 L 240 300 L 420 300 L 420 150 L 660 150",  // SVGパス
  progress:0.0 }                     // 0→1。ドット位置とルート実線に使う

// 金額計算(定数は1箇所に)
subtotal   = Σ(unitPrice × qty)
serviceFee = Math.floor(subtotal * SERVICE_RATE)   // SERVICE_RATE = 0.05
total      = subtotal + deliveryFee + serviceFee

route をSVGパス文字列で持つのが要点です。配達員ドットは offset-path: path(var(--route))、ルートの実線は同じパスの stroke-dashoffsetprogress から計算。1つのデータで2つの表現が同期します。


11. デモデータ

11.1 店舗(10件)

id 店名 ジャンル ETA 送料 最低注文 評価 件数 状態
st01 釜飯 とよ 和食 18-28分 ¥150 ¥800 4.6 212 営業中
st02 タイ食堂 レック アジア 22-32分 ¥200 ¥1,000 4.4 389 営業中
st03 NINE BURGER バーガー 15-25分 ¥0 ¥1,200 4.2 741 営業中
st04 手打ちそば 苔 和食 25-35分 ¥250 ¥900 4.7 96 営業中
st05 カリー トモ カレー 20-30分 ¥150 ¥800 4.5 458 営業中
st06 デリカ もり 惣菜 12-20分 ¥100 ¥600 4.1 167 営業中
st07 PIZZA FUMO ピザ 28-40分 ¥0 ¥1,800 4.3 523 営業中
st08 ゆるり珈琲 カフェ 15-22分 ¥180 ¥500 4.8 134 営業中
st09 洋菓子 あお スイーツ 20-30分 ¥200 ¥700 4.6 88 準備中
st10 まるとくマート 食料品 30-45分 ¥300 ¥1,500 3.9 1,024 営業中

★ETAと送料を意図的にばらけさせています。全店が同条件だとフィルタ(配達が速い 送料無料)が機能しているように見えません。st03 st07 の送料¥0、st06 の12-20分がフィルタの効きを示します。

11.2 メニュー(40品・st01を全量、他店は要約)

釜飯 とよ(st01)

id カテゴリ 商品名 説明 価格 品切れ
mi01 人気 五目釜飯 季節の具材を土鍋で炊き上げます 980
mi02 人気 鶏ごぼう釜飯 香ばしいごぼうと柔らかい鶏 1,080
mi03 サイド だし巻き玉子 出汁をたっぷり含ませています 480
mi04 ドリンク ほうじ茶(冷) 自家焙煎 220

オプション構成は§10の mi01 を全商品の雛形にし、カテゴリごとに内容を変える

カテゴリ 必須オプション 任意オプション
ごはん類 サイズ(並/大/特盛) トッピング(最大3)
バーガー セット(単品/ポテト/ドリンク付) トッピング(最大3)
ドリンク サイズ(S/M/L)・氷(あり/なし)
ピザ サイズ(M/L)・生地(薄/厚) トッピング(最大3)
スイーツ メッセージプレート(+¥300)

他9店舗は各4品(人気2・サイド1・ドリンク1)で構成。全40品に写真seed({店舗seed}-m1-m4)を割り当てます。

11.3 進行中の注文(初期状態)

初期は注文なしにします。読者が自分でカートに入れて注文する体験を通してほしいためです。

ただしD7注文履歴には完了済み3件を用意し、もう一度注文する から1タップで注文できるようにします。急いでいる読者はこちらから90秒シーケンスに入れます。

店舗 注文日時 内容 合計
st05 カリー トモ 7/21 12:34 チキンカレー(辛さ中辛・ライス大)× 1、ラッシー × 1 ¥1,690
st03 NINE BURGER 7/18 19:02 ダブルチーズ(ポテトセット)× 2 ¥2,760
st08 ゆるり珈琲 7/16 09:15 ドリップ(M・氷なし)× 2、スコーン × 1 ¥1,340

11.4 配達員(4名・ローテーション)

id 名前 車両 評価
c01 佐々木 自転車 4.9
c02 バイク 4.7
c03 ナディア 自転車 5.0
c04 三好 軽自動車 4.6

11.5 D8 配達員モードの依頼(3件)

店舗 受取 配達先 距離 報酬
st01 釜飯 とよ 蔵前1丁目 浅草橋3丁目 2.1km ¥620
st03 NINE BURGER 蔵前3丁目 蔵前1丁目 0.9km ¥480
st10 まるとくマート 両国2丁目 蔵前4丁目 3.4km ¥880

本日の実績:完了 8件 / 走行 21.4km / 報酬 ¥5,240


12. ディレクトリ構成

souzoh-delivery/
├── scripts/
│   └── fetch-images.mjs        ← Portalからコピー(無変更)
├── public/images/              ← .gitignore対象
├── src/
│   ├── theme.css               ★差し替え(§5.4)
│   ├── app.config.js           ★差し替え(下記)
│   ├── motion.js               ← Portalからコピー + G〜Nを追記
│   ├── config/demo.js          ★新規:DEMO_SPEED / SERVICE_RATE 等の定数
│   ├── data/
│   │   ├── stores.js  menuItems.js  orders.js
│   │   ├── couriers.js  jobs.js  imageManifest.js
│   ├── components/ui/          ← 母艦+Market+Portalからコピー
│   │   ├── SmartImage.jsx      ← Portal(無変更)
│   │   ├── Price.jsx           ← Market(無変更)★本作で6箇所に使用
│   │   ├── Carousel.jsx        ← Market(無変更)
│   │   ├── StepBar.jsx         ★新規:Stepperの横向き版(動きJ)
│   │   ├── LiveBand.jsx        ★新規:常駐バンド(§5.2・動きG)
│   │   ├── QtyStepper.jsx      ★新規:− 1 +
│   │   └── SwipeRow.jsx        ★新規:スワイプ削除(動きM)
│   ├── components/shell/       ← 母艦からコピー(LiveBandの高さ分だけ下部余白を確保)
│   ├── features/
│   │   ├── stores/  store-detail/  search/  history/
│   │   ├── customize/          ★新規:D3
│   │   ├── cart/               ★新規:D4
│   │   ├── checkout/           ★新規:D5
│   │   ├── tracking/           ★新規:D6
│   │   │   ├── RouteMap.jsx    ★実地図に差し替える場合の唯一の変更点(§3.3)
│   │   │   └── map-base.svg    ★抽象マップの下地
│   │   ├── courier/            ★新規:D8
│   │   └── chat/               ← 母艦 dm/ をそのまま
│   ├── store.js                ★カートと注文進行を持つ。本作で最も厚い
│   └── main.jsx
└── README.md
// app.config.js
export default {
  appName: "Souzoh Delivery",
  tagline: "いま、どこにいるかが見える",
  layout: { maxWidth: 600 },
  nav: [
    { key:"stores",  label:"お店",     icon:"Store" },
    { key:"search",  label:"さがす",   icon:"Search" },
    { key:"cart",    label:"カート",   icon:"ShoppingBag", badge:"cartCount" },
    { key:"history", label:"注文履歴", icon:"Receipt" },
    { key:"mypage",  label:"マイページ", icon:"User" },
  ],
  labels: {
    listTitle: "お届けできるお店",
    itemNoun: "商品",
    createCta: "カートに追加",
    primaryAction: "お気に入り",
  },
};

navbadge プロパティを追加しています。カート個数を表示するためで、母艦の shell/ に汎用のバッジ対応を入れてください(他アプリでは未使用のまま無害です)。


13. 実装手順(Claude Codeへの投げ方)

5分割。 Reviewsより1つ多いのは、カート(D3・D4)が独立した重さを持つためです。

Session やること 冒頭で読ませるもの
0 母艦を複製 → Portal/Marketから fetch-images.mjs SmartImage Price Carousel motion.js をコピー → theme.css app.config.js data/ config/demo.js を差し替え → npm run fetch:images で62枚取得shell/ にバッジ対応を追加 → ビルドを通す §5.4・§6・§11・§12
1 ui/ の4点(StepBar / LiveBand / QtyStepper / SwipeRow)+ motion.js にG〜N追記 + store.js のカート/注文の状態設計 §5.2・§7.2・§10
2 D1店舗一覧 + D2店舗詳細 §9(D1,D2)
3 D3カスタマイズ + D4カート + D5注文確定(金額計算をここで完成させる) §9(D3〜D5)・§10
4 D6配達追跡(抽象マップ・ルート・タイマー・§7.3のシーケンス) §3.3・§7.3・§8.2・§9(D6)
5 D7履歴 + D8配達員モード + D9検索 + 仕上げ §9(D7〜D9)・§14

Session 1で store.js の状態設計を先に固めてください。 本作の難所はUIではなくカートの状態です。「同一商品でもオプションが違えば別行」「別店舗の商品追加時はカートを空にする」「削除の取り消し」——ここを画面と一緒に作ると、D4で必ず作り直しになります。状態だけ先に設計し、コンソールで動作確認してから画面に入るのが最短です。

Session 4 の指示文テンプレ

添付の要件定義書 §3.3・§7.3・§8.2・§9(D6) を実装してください。
src/components/ui/ の StepBar / LiveBand / SmartImage と src/motion.js は完成済みです。
新規作成せず import して使ってください。
地図は外部ライブラリを使わず、静的SVG(map-base.svg)+ order.route のパス文字列で
実装してください。配達員ドットは offset-path、ルートの実線は stroke-dashoffset を使い、
どちらも order.progress の単一の値から算出してください。
進行速度は src/config/demo.js の DEMO_SPEED のみを参照し、
JSX に秒数を直接書かないでください。
実装前に、作成・変更するファイルの一覧だけ先に提示してください。

14. 受け入れ基準

条件①(デザイン)

  • ETAと金額が極太コンデンス(--font-num)+ tabular-nums
  • オレンジが進行中の状態以外に使われていない
  • 状態が色だけで区別されていない(アイコン形状かラベルを併記)
  • カードに影がない(常駐バンドのみ例外)
  • 生の色コード・生のpx値がJSXに1つも無い
  • §5.6のNGリストに1つも該当しない

条件②(画像)

  • npm run fetch:images で62枚が取得される
  • public/images/ を空にしても壊れた画像アイコンが出ない
  • 配達員アバターに実在人物の写真が使われていない
  • public/images/.gitignore に入っている

条件③(動き)

  • §7.2のG〜N、§7.1の継承分がすべて動作する
  • §7.3の90秒シーケンスが通り、D6を開いていなくても進行する
  • DEMO_SPEED を1に変えると実時間になる
  • prefers-reduced-motion: reduce で「停止」列がすべて止まる

条件④(第一印象)

  • D6でETAが減り、配達員ドットが動き、ルートの実線が伸びる(3つが同期)
  • 常駐バンドがどの画面でも表示され、状態変化でテキストが入れ替わる

機能(本作の要)

  • 同一商品でもオプションが違えばカートで別行になる
  • 別店舗の商品を追加しようとすると確認ダイアログが出る
  • オプション選択で カートに追加 ¥○○ の金額がリアルタイム変化する
  • 必須オプション未選択では追加できない/任意オプションは最大3つで打ち止め
  • 最低注文金額未達で あと ¥○○ が表示され、確定できない
  • スワイプ削除に 元に戻す が効く
  • もう一度注文する でオプションまで復元される
  • 置き配を選ぶと置き場所の3択が出現する
  • 合計 = 商品小計 + 配送料 + サービス料5% が全画面で一致する
  • D8配達員モードが4ボタンで順送りできる

共通

  • 390px / 1024px の2幅で崩れない
  • npm run build が警告なく通り、Cloudflare Pagesで表示される

15. シリーズ資産の状況(6本目終了時点)

資産 出所 本作での扱い
components/ui/ 8点 母艦SNS そのまま
components/shell/ 母艦(+maxWidth +badge) 本作でbadge対応を追加
Price.jsx Market 6箇所で使用。投資回収
Carousel.jsx Market メニュー写真に流用
Stepper.jsx Market StepBar.jsx(横向き)に派生
SmartImage.jsx Portal そのまま
fetch-images.mjs Portal そのまま
motion.js Portal コピー+追記
Stars Reviews 店舗評価表示に流用
QtyStepper / SwipeRow / LiveBand 本作 → 学習アプリのカート的UIに転用可

新規実装は ui/ 4点と画面5枚。 6本目にしてもなおD3・D4・D6という重い3画面があるため、Reviewsほど軽くはなりません。ただし前5本が無ければ、この規模は成立していません。


16. 補足:コスト感

Marketと並ぶ重量級です。Marketが「状態遷移の複雑さ」、Portalが「演出の作り込み」だったのに対し、本作はカートの状態設計が重さの中心です。

費用が伸びる箇所は事前に特定できています。

  1. カートの状態設計(オプション違いの同一商品、別店舗の扱い、削除の取り消し) → §13のSession 1で画面と切り離して先に作ることで潰します
  2. 配達アニメーションの3要素同期(ドット位置・ルート実線・ETA) → §10で progress という単一の値から全部を導出する設計にしてあります。3つを別々に管理すると必ずズレて往復が発生します
  3. 時間進行のデバッグDEMO_SPEED を1定数にしてあるので、開発中は100倍速で確認できます

逆に、条件②③がPortal資産の持ち込みで済むこと、金額表示がMarketの Price.jsx で統一済みであることが効いています。シリーズを1本目から順に作ってきた効果が、最も金額として表れるのがこの1本です。

FROM DEMO TO PRODUCTION

本番で使うには、あと4つ必要です

この要件定義書は、画面と動きを確かめるためのデモとして書かれています。「デリバリー」を社内やお客さまに実際に使ってもらうには、見た目の裏側に次の4つが要ります。

  1. 1データの保存

    デモでは
    データはその端末のブラウザの中にしか残りません。ほかの端末やほかの人とは共有されず、消えることもあります。
    本番では
    サーバーのデータベースに保存し、バックアップと復元ができるようにします。
  2. 2セキュリティ

    デモでは
    ログインがなく、誰でもすべての画面とデータを見られます。
    本番では
    ログイン、権限(誰が何を見られるか)、通信とデータの暗号化、不正なアクセスへの対策を入れます。
  3. 3ガバナンス(運用ルール)

    デモでは
    決めていません。
    本番では
    誰がいつ何をしたかの記録、個人情報の扱い(プライバシーポリシー・同意)、アカウントの発行と削除、障害のときの連絡体制を決めます。
  4. 4公開・デプロイ

    デモでは
    手元で動かすか、デモとして公開するだけです。
    本番では
    独自ドメイン、本番とテストの環境分け、更新の手順、監視と障害対応、月々の費用の管理を整えます。

本番運用をお考えの方へ

ぜひ一度ご相談ください。最適なプランのご紹介と、進め方をお伝えします。代表の西澤が直接お話しします。

  • 自分で作ったアプリを、そのまま本番で使いたい
  • どこまで自分で作り、どこから任せるか決めたい
  • 社内の決まりやセキュリティの基準に合わせたい

下のカレンダーから、そのまま日程を選べます。

カレンダーが表示されないときは こちらから日程を選べます。日程を決める前に聞きたいことがあれば、公式LINEお問い合わせフォーム からどうぞ。

FREE CONSULTATION

分からないときは、お気軽に30分無料相談へ

途中で止まった、エラーが消えない、自社向けに作り変えたい。どんなことでも大丈夫です。

日程を決めて話す

代表の西澤と直接お話しできます。空いている日時を選ぶだけで予約できます。

代表 西澤と話す日程を選ぶ

まずは問い合わせる

要件定義書がほしい方・ご相談は公式LINEから。メールでのお問い合わせはフォームから。