SOUZOHSPECS

一般向けアプリ ・ Souzoh Reviews

口コミ・レビュー

星の数より、その中身を。評価の理由が見えるレビュー

  1. 要件定義書を保存する

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    Session 0母艦を複製 → Portalから fetch-images.mjs SmartImage.jsx motion.js をコピー → theme.css app.config.js data/ を差し替え → npm run fetch:images で57枚取得 → 画面は触らずビルドを通す
    Claude Code に送る
    @要件定義書.md の「実装手順」にある Session 0 を作ってください。
    Session 1ui/ の6点(Stars / StarInput / ScoreBig / Distribution / AspectBars / Lightbox)+ motion.js にA〜F追記。画面はまだ作らない
    Claude Code に送る
    @要件定義書.md の「実装手順」にある Session 1 を作ってください。
    Session 2R2 店舗詳細(§7.3のシーケンスまで完成させる)
    Claude Code に送る
    @要件定義書.md の「実装手順」にある Session 2 を作ってください。
    Session 3R3 レビュー投稿(プレビュー+分布の再計算)+ R1 発見
    Claude Code に送る
    @要件定義書.md の「実装手順」にある Session 3 を作ってください。
    Session 4R4検索 + R5一覧 + R6レビュアー + R7マイページ + 仕上げ
    Claude Code に送る
    @要件定義書.md の「実装手順」にある Session 4 を作ってください。
  6. 自分用に変える・公開する 必要な人だけ

    Claude Code に送る
    この要件定義書は、記事に埋め込むデモ用に書かれています。
    自分の会社で実際に使えるように変えたいです。外したほうがいいところと、残したほうがいいところを教えてください。
    Claude Code に送る
    このアプリをインターネットに公開したいです。
    無料でできる方法を、1手順ずつ案内してください。

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

口コミ・レビューアプリ テンプレート 要件定義書

プロダクト名:Souzoh Reviews

シリーズ5本目。Portalで作った共通資産(画像取得スクリプト・SmartImage・motion.js)を初めて全面的に受け取る1本です。 前作で追加した4条件(デザイン/画像自動取得/動き/第一印象)は本書でも必達要件として引き継ぎます。


1. 目的とゴール

項目 内容
コンセプト 地域のお店(飲食・美容・整体・ジムなど)の口コミを、評価の中身が見える形で集めるレビューアプリ
一次目的 星の数だけでなく「なぜその評価なのか」が伝わるUIを見せる
二次目的 店舗業の読者に「自社の口コミ基盤が欲しい」と思わせる。Souzohの既存クライアント層(整体・ジム・ヨガ)と直結する題材
成功条件 評価分布バーと項目別スコアが動くところまで読者が到達する
非ゴール 実運用可能な口コミプラットフォーム。不正検知・通報対応・店舗審査

なぜこの題材か:レビューはデータの可視化と文章コンテンツが同居する唯一の題材です。Portalが数字と写真の同居だったのに対し、こちらは「分布グラフ」「項目別スコア」「写真付きレビュー」が1画面に載る。しかも店舗業は読者の母数が最も大きい。


2. 引き継ぐ4条件

条件 対応章 本作での実現方法
① デザインにこだわる §5 エディトリアル(雑誌)路線。クリーム地+セリフ見出し。前4本と完全に別方向
② ネットから画像を自動取得 §6 Portalの fetch-images.mjsそのまま流用。マニフェストだけ差し替え
③ 動きをつける §7 Portalの motion.js を継承 + 本作固有の6種を追加
④ 一目ですごい §8 「星の部分塗り」と「分布バーの積み上がり」に集中投下

★ここがシリーズ設計の効果が最も出る1本です。②はスクリプトを持ってくるだけ、③は定数ファイルを持ってくるだけで条件を満たします。実質的な作り込みは①④に全振りできます。


3. スコープ

3.1 作るもの

# 画面 優先度 母艦/Portalとの関係
R1 発見(ホーム) A 母艦S1を改修
R2 店舗詳細 S 新規。評価サマリーが本体
R3 レビュー投稿 S 新規。星+項目別+写真
R4 検索・絞り込み A Market K3を流用
R5 レビュー一覧・詳細 A 母艦S1/S2を改修
R6 レビュアー(投稿者)ページ B 母艦S5を改修
R7 マイページ B Market K6を改修
R8 保存したお店 C 母艦の一覧を流用

3.2 作らないもの

  • ❌ バックエンド・DB・認証・データ永続化
  • ❌ 地図表示(理由は§3.3
  • ❌ 予約・決済・クーポン
  • ❌ 通報・削除申請・店舗からの返信管理
  • ❌ ダークモード、多言語、テストコード

3.3 地図を入れない判断

口コミアプリで地図は自然な発想ですが、外します。

  • 地図ライブラリ(Leaflet / MapLibre)はバンドルが重く、タイル配信という新しい外部依存を1つ増やします。§6で外部依存を潰す設計にしているのと矛盾します
  • タイルサービスは商用利用の条件が提供者ごとに違い、配布テンプレートに載せると利用者に判断を押し付けることになります
  • デモとしての見せ場が薄い。地図は「動いて当たり前」で、驚きに繋がりません

代わりにエリアチップ+徒歩分数の表記で位置情報を扱います。実装ゼロで体験は成立します。

3.4 デモデータの注意(Marketより厳しく扱う)

実在の店舗名・チェーン名を一切使いません。 Marketでブランド名を排除したのと同じ理由に加えて、こちらは低評価レビューを載せるという事情があります。架空店舗に星2のレビューが付いているのは何の問題もありませんが、実在店舗名が入った瞬間に風評の話になります。

店名はすべて架空のものを使い、README と画面フッターに「掲載されている店舗・レビューはすべて架空のものです」を明記してください。


4. 技術構成

Vite + React 18(JSX)
Tailwind CSS v4
lucide-react
motion(motion.dev)… Portalと同じく使用範囲を限定(§7.4)
────────── Portalから持ち込む ──────────
scripts/fetch-images.mjs
src/components/ui/SmartImage.jsx
src/motion.js
────────── 本作で追加なし ──────────
recharts は使いません

recharts を外す理由:本作のグラフは「横バー5本の積み上がり」と「項目別スコアのバー」だけです。どちらもdivとCSSで15行、軸も目盛りもツールチップも要りません。Portalとは逆の判断ですが、逆にすべき理由があります——ここで自前実装にしておくと、バーの動きをmotion.jsの定数で完全に制御できます。ライブラリのアニメーションと自前の演出が混ざるほうが、調整の往復が増えます。


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

5.1 方向性

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

アプリ 基調 書体 質感
SNS 明るいウォームグレー サンセリフ 余白
Match ディーププラム サンセリフ 舞台
Market サンセリフ(極太数字) 陳列
Portal ディープインク サンセリフ ガラス/管制室
Reviews クリーム(紙) セリフ見出し 雑誌/エディトリアル

5本目にして初めてセリフ体を使います。 レビューは文章が主役のコンテンツなので、書評誌や食のムック本のような佇まいが最も合います。加えて、4本がサンセリフで揃っている中に1本だけ紙質のセリフを入れると、5枚並べたときの記憶に残る1枚になります。

具体的には、クリーム地に細い罫線と大きな見出し。写真は角丸を極小(4px)にして紙面に貼った印象を作ります。差し色は墨に近い濃紺と、星のためのテラコッタ。

5.2 シグネチャー要素:星の部分塗り

評価 3.7 を星4つ目まで塗るのではなく、4つ目の星を70%だけ塗ります

★★★☆☆   ← やらない(3.7が3.0に見える)
★★★◐☆   ← これ。SVGのlinearGradientで70%の位置に色境界を置く

実装は <linearGradient>offset を評価値から計算するだけ(約12行)。しかしこの1点でアプリの精度の印象が変わります。星が並ぶ画面で部分塗りができているかどうかは、作り込みの水準がそのまま出る箇所です。ここを最初に作り切ってください。

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

@theme {
  /* ---- Base ---- */
  --color-bg:        #FBF8F1;  /* クリーム:わずかに黄み。紙の地 */
  --color-surface:   #FFFFFF;  /* 記事面・カード */
  --color-surface-2: #F3EEE3;  /* 入れ子・入力欄 */
  --color-ink:       #1A1712;  /* 墨に近い黒 */
  --color-ink-sub:   #6E675C;
  --color-ink-dim:   #A39B8D;
  --color-line:      #E2DACB;  /* 罫線。多用してよい */
  --color-line-bold: #1A1712;  /* 見出し下の太罫(2px) */

  /* ---- Accent ---- */
  --color-star:      #E0703A;  /* テラコッタ:星のみに使う */
  --color-accent:    #1F3A5F;  /* 濃紺:CTA・リンク */
  --color-good:      #4A7C59;  /* 高評価の分布バー */
  --color-mid:       #C4A24C;  /* 中評価 */
  --color-low:       #A85A50;  /* 低評価。赤にしない */

  /* ---- Type ---- */
  --font-display: "Shippori Mincho B1", "Playfair Display", serif;  /* 600 */
  --font-body:    "Noto Sans JP", sans-serif;                       /* 400/500 */
  --font-num:     "Bodoni Moda", "Noto Serif JP", serif;            /* ★評点はセリフ数字 */

  --text-hero:   clamp(30px, 3.6vw, 46px)/1.25;
  --text-score:  clamp(40px, 5vw, 64px)/1.0;   /* 店舗の総合評点 */
  --text-title:  21px/1.5;
  --text-body:   15px/1.9;   /* ★行間を全アプリ中いちばん広く。読み物なので */
  --text-label:  11px/1.3;

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

  /* ---- Shape ---- */
  --radius-card:  8px;    /* 全アプリ中いちばん小さい。紙に貼る印象 */
  --radius-photo: 4px;
  --radius-pill:  999px;
  --shadow-card:  none;             /* ★影は使わない。罫線で構造を作る */
  --shadow-pop:   0 16px 48px rgba(26,23,18,.18);  /* モーダルのみ */

  /* ---- 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.4 タイポグラフィの決め事

  • 評点だけはセリフ数字--font-num = Bodoni系)。4.3 が大きくセリフで組まれているだけで、雑誌の評価記事の顔になります
  • 見出しは --font-display(明朝)。本文は明朝にしない——日本語の長文明朝は画面で読みにくく、レビュー本文はサンセリフのほうが機能します
  • セクション見出しの下に 2pxの太罫--color-line-bold)。エディトリアル感の最大の供給源で、実装コストはゼロです
  • 本文行間1.9。5本中で最も広く取ります

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

  • ❌ 星を絵文字(⭐)で描く。必ずSVG(部分塗りができないため)
  • ❌ 低評価に赤(#FF0000系)を使う。--color-low の落ち着いた赤茶に留める
  • ❌ カードへの影(--shadow-card: none
  • ❌ 本文の明朝組み
  • ❌ 角丸の混在(3種のみ)
  • ❌ 「★4.3」のようにテキストで星を書く箇所を残す

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

6.1 方針

Portalの scripts/fetch-images.mjs をそのままコピーします。 差し替えるのは imageManifest.js だけです。仕様(ビルド時取得・4本並列・失敗は握りつぶす・--forcepublic/images/.gitignore)はPortal §6.3と完全に同一なので、本書では繰り返しません。

表示側も SmartImage.jsx の3層フォールバック(ローカル → 外部seed URL → グラデーション)をそのまま使います。

6.2 本作固有の事情:写真がコンテンツそのもの

前4本では写真は装飾でしたが、レビューアプリでは写真が投稿内容の一部です。「写真付きレビュー」というラベルが機能するので、取得枚数を増やします。

用途 枚数
店舗のカバー写真 12
店舗の内観・商品写真(詳細のギャラリー) 24(1店舗あたり2枚)
レビュー添付写真 18
特集バナー 3
合計 57

Portalの18枚から3倍ですが、取得は並列4本で30秒程度です。ここは惜しまないでください。写真の枚数がそのまま「賑わっている感」になります。

6.3 レビュアーのアバター

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

本作では特に重要で、架空の低評価レビューに実在の人物の顔が添えられるのは避けるべき状態です。

6.4 マニフェスト抜粋(src/data/imageManifest.js

export default [
  // 店舗カバー(12)
  { seed:"shop-cafe-hazama",   w:1400, h:900, use:"喫茶 はざま" },
  { seed:"shop-ramen-kiyo",    w:1400, h:900, use:"らーめん 清雅" },
  { seed:"shop-seitai-nagi",   w:1400, h:900, use:"整体院 なぎ" },
  { seed:"shop-gym-forge",     w:1400, h:900, use:"FORGE 24" },
  { seed:"shop-yoga-tsumugi",  w:1400, h:900, use:"ヨガスタジオ つむぎ" },
  { seed:"shop-bakery-kona",   w:1400, h:900, use:"パン工房 こな" },
  { seed:"shop-izakaya-tobi",  w:1400, h:900, use:"炭火酒場 とび" },
  { seed:"shop-salon-lume",    w:1400, h:900, use:"hair salon LUME" },
  { seed:"shop-curry-mame",    w:1400, h:900, use:"スパイスカレー まめ" },
  { seed:"shop-sento-yumoto",  w:1400, h:900, use:"銭湯 湯もと" },
  { seed:"shop-book-yoruba",   w:1400, h:900, use:"夜間書店 よるば" },
  { seed:"shop-sushi-isana",   w:1400, h:900, use:"鮨 いさな" },

  // 店舗ギャラリー(24)… {seed}-a / {seed}-b を各店に
  // レビュー添付(18)… rv-01 〜 rv-18、比率は 4:3 と 1:1 を混在
  // 特集バナー(3)
  { seed:"feature-morning", w:1600, h:600, use:"特集:朝から開いている店" },
  { seed:"feature-solo",    w:1600, h:600, use:"特集:ひとりで入りやすい店" },
  { seed:"feature-rain",    w:1600, h:600, use:"特集:雨の日に行きたい店" },
];

7. 動き ★条件③

7.1 Portalからの継承

src/motion.js をコピーし、以下をそのまま使います。

# 名称 本作での適用先
1 段差入場 R1の店舗カード(60ms差)
4 スクロール連動リビール R2のレビュー一覧、R5
5 ホバー浮上 店舗カード(影がないので枠色と写真の微拡大で表現)
6 View Transitions 画面遷移 全ページ
7 共有要素モーフ 店舗カード → R2(カバー写真と店名が連続移動)
9 リスト入れ替え R4の絞り込み結果
14 トースト 全体

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

# 名称 対象 仕様 reduced時
A 星の充填 R2の総合評価 5つの星が左から順に塗られる。1つあたり110ms、4つ目は70%で止まる 完成形を即表示
B 分布バーの積み上がり R2の評価分布 星5〜1の横バーが下から上の順に幅0→実値、各180ms/80ms差 完成形
C 評点カウントアップ R2の 4.3 0.0→4.3、小数第1位まで、900ms。--font-num 即表示
D 項目別スコアバー R2の項目別 4項目のバーが左から伸びる、各400ms/60ms差 完成形
E 星の入力 R3 ホバー/ドラッグで星が追従。確定時に選んだ星が1度だけ12%拡大→戻る(180ms) 拡大なし
F 写真ライトボックス R2ギャラリー・R5 サムネから全画面へ共有要素モーフで拡大(--dur-base フェード

BとCを同時に走らせます。 評点が0.0から4.3へ数えあがる横で、分布バーが下から積み上がる。この0.9秒が本作の"すごい"の中心です(Portalのカウントアップ+スパークラインと同じ設計思想)。

7.3 R2 到達時のシーケンス

0ms     カバー写真がモーフ着地
120ms   店名・エリア・営業状態が fade-in
200ms   評点カウントアップ開始         ← C
        同時に星の充填開始             ← A
        同時に分布バーが下から積み上がり ← B
1100ms  上記3つが着地
1200ms  項目別スコアバーが伸びる        ← D
1600ms  完了。以降は静止

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

  • ✅ #7 共有要素モーフ(店舗カード → R2)
  • ✅ F 写真ライトボックス
  • ✅ #9 リスト入れ替え
  • ❌ 上記以外はCSS / WAAPI / View Transitions

A〜Dはすべて自前です(§4の通り)。CSS変数 --w を JS から書き換えて transition: width させるだけで、motion.js の定数と完全に同期します。


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

8.1 勝負するのはR2(店舗詳細)

Portalはホームが勝負でしたが、本作は店舗詳細です。レビューアプリの本質はホームの一覧ではなく「1店舗を深く見る」体験にあり、記事のスクショもここから撮ります。

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

  1. 大きなセリフの評点4.3)が数えあがっている
  2. 部分塗りの星(4つ目が70%)
  3. 評価分布の横バー5本が積み上がっている
  4. 項目別スコア(味・接客・雰囲気・価格)
  5. カバー写真が発色している
  6. 営業中 / 22:00まで のライブ表示

「分布」が載っていることが決定的です。 平均点だけのレビューUIは無数にありますが、分布が見えると「星4.3の中身は5が多数+2がわずか」という情報が伝わります。ここが本作の主張であり、そのまま見た目の情報量になります。

8.2 R2 のレイアウト(PC 1120px時)

┌──────────────────────────────────────────────┐
│ カバー写真(1400×900をトリミング、高さ320px)    │
│  ← 下端に --color-bg へのグラデーション         │
├────────────────────┬─────────────────────────┤
│ 店名(--font-display)│  評点 4.3(--text-score)  │
│ ジャンル・エリア・徒歩5分│  ★★★★◐               │
│ 営業中 / 22:00まで    │  レビュー 128件            │
│                     ├─────────────────────────┤
│                     │  ★5 ████████████ 78      │
│                     │  ★4 ██████ 31            │
│                     │  ★3 ███ 12               │
│                     │  ★2 █ 5                  │
│                     │  ★1 █ 2                  │
├────────────────────┴─────────────────────────┤
│ 項目別 味 4.5 / 接客 4.2 / 雰囲気 4.6 / 価格 3.8 │
├──────────────────────────────────────────────┤
│ ギャラリー(横スクロール・2枚 + レビュー写真)      │
├──────────────────────────────────────────────┤
│ レビュー一覧(並び替え・絞り込み付き)             │
└──────────────────────────────────────────────┘

モバイルは縦積み。評点ブロックは店名の直下に横並び(評点|星|件数)。

8.3 「参考になった」の扱い

各レビューに 参考になった 12 ボタンを置きます。押すと数字がスライドカウントアップし、そのレビューが並び替え「参考になった順」で1つ上がるところまで動かします。

小さい実装ですが、押した結果が並び順に反映されるのはデモとして効きます。押して終わりのボタンとは印象が違います。


9. 画面仕様

R1. 発見(ホーム)

  • ヘッダー:検索バー + エリアチップ(蔵前 浅草橋 両国 門前仲町 清澄白河
  • 特集バナー3枚(横スクロール、feature-* 画像)
  • いま評価が伸びている店 … 6件のカード
  • 写真が多い店 … 6件
  • ジャンルチップ:カフェ ラーメン 居酒屋 整体 ジム ヨガ 美容室 パン 銭湯 書店
  • 店舗カード:カバー写真/店名/評点+星/件数/ジャンル・エリア・徒歩分数/保存アイコン

R2. 店舗詳細 ★本体

§8.2の構成。加えて:

  • 営業状態の判定data の営業時間と現在時刻を比較して 営業中 / 22:00まで 本日休業 まもなく閉店 を出し分け。実装は10行、体験の解像度が上がります
  • 基本情報:住所(架空)・電話(03-0000-0000 形式のダミー)・定休日・支払い方法・席数
  • ギャラリー:店舗写真2枚 + レビュー添付写真。タップでライトボックス(F)
  • レビュー一覧:並び替え 新しい順 / 参考になった順 / 評価が高い順 / 評価が低い順、絞り込み 写真あり / 星で絞る
  • 下部固定:保存レビューを書く(濃紺)

R3. レビュー投稿 ★固有画面

全画面フォーム。上から:

# 項目 仕様
1 総合評価 大きな星5つ。タップ/ドラッグで0.5刻み(動きE)。未選択では投稿不可
2 項目別 接客 雰囲気 価格 の4つを小さい星で。任意(未入力可)
3 訪問時期 3択チップ:1か月以内 3か月以内 半年以上前
4 訪問シーン 4択チップ:ひとり 友人と 家族と 仕事で
5 本文 20〜1000字。20字未満は投稿不可+「もう少し詳しく教えてください」
6 写真 4枠。ダミー画像ピッカー(6枚から選択)

リアルタイムのプレビュー:フォーム下部に、投稿後の見え方をそのまま表示します。星・本文・写真が入力に追従して変化する。これがR3の見せ場で、実装は同じコンポーネントを再利用するだけです。

投稿 → R2のレビュー一覧先頭に挿入 + 評点・分布バー・件数が再計算されてアニメーションで更新(B・Cが再実行)+ トースト。

自分の投稿で分布バーが動くのが、このアプリで最も気持ちいい瞬間です。 必ず実装してください。

R4. 検索・絞り込み

  • 検索:店名・ジャンル・レビュー本文を部分一致
  • 絞り込みパネル:ジャンル エリア 評点(3.0以上/3.5以上/4.0以上) 写真あり 営業中のみ
  • 並び替え:評点が高い順 / レビューが多い順 / 新着順
  • 適用中の条件をチップ表示、×で個別解除
  • 空状態:「条件に合うお店がありません」+ 評点の条件を外す

R5. レビュー一覧・詳細

  • 全店舗横断のレビューフィード(みんなの口コミ タブ)
  • レビューカード:レビュアー・星・訪問シーン・本文(3行で省略)・写真サムネ・参考になった
  • 詳細:本文全文 + 写真ギャラリー + 店舗への導線

R6. レビュアーページ

  • アバター・表示名・投稿数・獲得した「参考になった」総数
  • 称号バッジ:投稿数に応じて はじめて 常連 達人(3段階のみ。細分化しない)
  • 投稿レビュー一覧

R7. マイページ

  • タブ:書いたレビュー / 保存したお店 / 下書き
  • サマリー:レビュー 8件 参考になった 47 訪問した店 12

10. データモデル

// shop
{ id:"sh01", name:"喫茶 はざま", genre:"カフェ",
  area:"蔵前", walkMin:5,
  cover:{ seed:"shop-cafe-hazama", w:1400, h:900 },
  gallery:[{seed:"shop-cafe-hazama-a"},{seed:"shop-cafe-hazama-b"}],
  hours:{ open:"08:00", close:"22:00", closedOn:["火"] },
  tel:"03-0000-0001", address:"東京都台東区蔵前0-0-0",
  payment:["現金","クレジットカード","QR決済"], seats:18,
  score:4.3, reviewCount:128,
  distribution:{ 5:78, 4:31, 3:12, 2:5, 1:2 },
  aspects:{ 味:4.5, 接客:4.2, 雰囲気:4.6, 価格:3.8 },
  saved:false }

// review
{ id:"rv01", shopId:"sh01", reviewerId:"u03",
  score:5, aspects:{味:5,接客:4,雰囲気:5,価格:4},
  visited:"1か月以内", scene:"ひとり",
  body:"...", photos:[{seed:"rv-01"}],
  helpful:24, marked:false, createdAt:"..." }

// reviewer
{ id:"u03", name:"あさひ", reviewCount:31, helpfulTotal:412,
  badge:"達人", avatarSeed:"u03" }

// 評点の再計算(投稿時にこれを走らせる)
score = Σ(星 × 件数) / 総件数  → 小数第1位で四捨五入
distribution[n] += 1
aspects[k] = 未入力を除外した平均

score をデータに持ちつつ再計算もする理由:初期表示は静的な値で即座に出し、投稿時のみ再計算します。全レビューから毎回集計すると初期表示が遅くなり、§7.3のシーケンスが崩れます。


11. デモデータ

11.1 店舗(12件)

id 店名 ジャンル エリア 徒歩 評点 件数 分布 5/4/3/2/1
sh01 喫茶 はざま カフェ 蔵前 5分 4.3 128 78/31/12/5/2
sh02 らーめん 清雅 ラーメン 浅草橋 3分 4.6 341 264/52/18/5/2
sh03 整体院 なぎ 整体 蔵前 7分 4.8 86 76/8/2/0/0
sh04 FORGE 24 ジム 両国 4分 3.9 212 92/64/33/15/8
sh05 ヨガスタジオ つむぎ ヨガ 清澄白河 6分 4.7 94 78/12/3/1/0
sh06 パン工房 こな パン 蔵前 2分 4.4 156 96/40/14/4/2
sh07 炭火酒場 とび 居酒屋 門前仲町 5分 4.1 267 142/72/33/13/7
sh08 hair salon LUME 美容室 両国 8分 4.5 73 51/16/4/1/1
sh09 スパイスカレー まめ カレー 浅草橋 6分 4.2 189 108/48/22/8/3
sh10 銭湯 湯もと 銭湯 蔵前 9分 4.0 132 62/40/20/7/3
sh11 夜間書店 よるば 書店 清澄白河 11分 4.6 58 44/10/3/1/0
sh12 鮨 いさな 寿司 門前仲町 3分 3.6 97 34/26/21/10/6

評点は意図的にばらけさせています。 全店が4.5以上のデモは嘘くさく、分布バーの見せ場も消えます。sh12(3.6)と sh04(3.9)が入っていることで、分布バーが「低評価も含めて可視化する」機能だと伝わります。

11.2 レビュアー(10名)

id 表示名 投稿数 参考になった総数 称号
u00 あなた 8 47 常連
u01 くらた 142 2,380 達人
u02 三日月 87 1,104 達人
u03 あさひ 31 412 常連
u04 ノブ 19 233 常連
u05 まめだ 64 891 達人
u06 陽子 12 96 常連
u07 りく 4 21 はじめて
u08 佐伯 55 702 達人
u09 ぽち 2 8 はじめて

11.3 レビュー(32件・抜粋)

高評価の例

店舗 投稿者 シーン 本文
sh01 u03 5 ひとり 平日の昼過ぎに行くと、だいたい席が空いています。豆の説明を聞くと長くなるので、時間があるときに行くのがおすすめです
sh02 u01 5 ひとり スープが変わりましたね。前より塩味が落ち着いて、最後まで飲めるようになりました
sh03 u06 5 ひとり 3回通って、朝起きたときの首の痛みがなくなりました。その日だけ楽になる施術とは違うと思います
sh05 u02 5 ひとり 初心者クラスでも、できないポーズを無理にやらせないのが良いです
sh11 u08 5 ひとり 22時以降の書店という時点で価値があります。品揃えは偏っていますが、その偏りが良い

中〜低評価の例(これがあることでデモの説得力が変わります)

店舗 投稿者 シーン 本文
sh04 u04 3 ひとり マシンは充実していますが、19〜21時は待ちが発生します。時間をずらせる人向けです
sh12 u05 3 仕事で 味は良かったです。ただこの価格帯なら、もう少し落ち着いて食べられる席の作りだと嬉しい
sh07 u09 2 友人と 金曜の夜に予約なしで行ったのが悪かったのですが、注文が通るまで30分かかりました
sh10 u07 3 ひとり お湯は良いです。ロッカーが古いのと、ドライヤーが有料なのは知っておくと良いかも
sh04 u06 4 ひとり 24時間なのが一番の価値。設備は値段なりですが、そこに文句を言う施設ではないと思います

低評価レビューの書き方に注意してください。 攻撃的な文面ではなく「事実 + 誰に向くか」の形にしています。配布テンプレートのデモデータとして適切であると同時に、実際に参考になる口コミの見本にもなります。

11.4 特集(3件)

タイトル 説明 掲載店
朝から開いている店 8時前に灯りがついている5軒 sh01 sh06 sh10
ひとりで入りやすい店 カウンターがある、静かに過ごせる sh01 sh02 sh11 sh09
雨の日に行きたい店 駅から近い、長居できる sh06 sh08 sh11

12. ディレクトリ構成

souzoh-reviews/
├── scripts/
│   └── fetch-images.mjs        ← Portalからコピー(無変更)
├── public/images/              ← .gitignore対象
├── src/
│   ├── theme.css               ★差し替え(§5.3)
│   ├── app.config.js           ★差し替え(下記)
│   ├── motion.js               ← Portalからコピー + A〜Fを追記
│   ├── data/
│   │   ├── shops.js  reviews.js  reviewers.js
│   │   ├── features.js  imageManifest.js
│   ├── components/ui/          ← 母艦+Portalからコピー
│   │   ├── SmartImage.jsx      ← Portalから(無変更)
│   │   ├── Stars.jsx           ★新規:部分塗り(§5.2)。最初に作る
│   │   ├── StarInput.jsx       ★新規:0.5刻み入力(動きE)
│   │   ├── ScoreBig.jsx        ★新規:セリフ数字+カウントアップ
│   │   ├── Distribution.jsx    ★新規:分布バー5本(動きB)
│   │   ├── AspectBars.jsx      ★新規:項目別(動きD)
│   │   └── Lightbox.jsx        ★新規:写真拡大(動きF)
│   ├── components/shell/       ← 母艦からコピー
│   ├── features/
│   │   ├── discover/  shop/  review-form/  search/
│   │   ├── reviews/  reviewer/  my-page/
│   ├── store.js
│   └── main.jsx
└── README.md
// app.config.js
export default {
  appName: "Souzoh Reviews",
  tagline: "星の数より、その中身を",
  layout: { maxWidth: 1120 },
  nav: [
    { key:"discover", label:"さがす",     icon:"Compass" },
    { key:"reviews",  label:"口コミ",     icon:"MessageSquareQuote" },
    { key:"write",    label:"投稿",       icon:"PenLine" },
    { key:"saved",    label:"保存",       icon:"Bookmark" },
    { key:"mypage",   label:"マイページ", icon:"User" },
  ],
  labels: {
    listTitle: "近くのお店",
    itemNoun: "お店",
    createCta: "レビューを書く",
    primaryAction: "保存",
  },
};

Marketと同じく、**タブバーの中央を「投稿」**にしています。口コミアプリで最も価値の高い行動は投稿です。


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

4分割。 Portal・Marketより1つ少ないのは、§2の通り条件②③を資産の持ち込みで満たせるためです。

Session やること 冒頭で読ませるもの
0 母艦を複製 → Portalから fetch-images.mjs SmartImage.jsx motion.js をコピー → theme.css app.config.js data/ を差し替え → npm run fetch:images で57枚取得 → 画面は触らずビルドを通す §5.3・§6・§11・§12
1 ui/ の6点(Stars / StarInput / ScoreBig / Distribution / AspectBars / Lightbox)+ motion.js にA〜F追記。画面はまだ作らない §5.2・§7.2・§10
2 R2 店舗詳細(§7.3のシーケンスまで完成させる) §7.3・§8.2・§9(R2)
3 R3 レビュー投稿(プレビュー+分布の再計算)+ R1 発見 §9(R1,R3)・§10
4 R4検索 + R5一覧 + R6レビュアー + R7マイページ + 仕上げ §9(R4〜R7)・§14

Session 1で ui/ を先に作り切ることが本作の要点です。 星コンポーネントは全画面に出てきます。ここが後回しになると、画面ごとに微妙に違う星が量産され、統一の手戻りが発生します(Marketの Price と同じ構図です)。

Session 2 の指示文テンプレ

添付の要件定義書 §7.3・§8.2・§9(R2) を実装してください。
src/components/ui/ の Stars / ScoreBig / Distribution / AspectBars / SmartImage と
src/motion.js は完成済みです。新規作成せず import して使ってください。
§7.3 のシーケンス(0ms〜1600ms)をそのままの順序・時間で再現してください。
アニメーションの時間・遅延は src/motion.js の定数のみを参照し、
JSX に数値を直接書かないでください。
色・余白・角丸は src/theme.css の変数のみを使ってください。
実装前に、作成・変更するファイルの一覧だけ先に提示してください。

14. 受け入れ基準

条件①(デザイン)

  • 星がSVGで描かれ、小数評価が部分塗りで表現されている(4.3なら4つ目が30%)
  • 評点がセリフ体(--font-num)で組まれている
  • セクション見出しの下に2pxの太罫がある
  • カードに影が使われていない
  • 生の色コード・生のpx値がJSXに1つも無い
  • §5.5のNGリストに1つも該当しない

条件②(画像)

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

条件③(動き)

  • §7.2のA〜F、§7.1の継承分がすべて動作する
  • §7.3のシーケンスが順序・時間通りに走る
  • prefers-reduced-motion: reduce で「停止」列がすべて止まる
  • View Transitions 非対応でも遷移が壊れない

条件④(第一印象)

  • R2到達時に、評点カウントアップ・星の充填・分布バーの積み上がりが同時に走る
  • R2ファーストビューに §8.1 の6要素が同時に見える

機能

  • R3で総合評価未選択・本文20字未満のとき投稿できない
  • R3のプレビューが入力にリアルタイム追従する
  • 投稿すると評点・件数・分布バーが再計算され、アニメーションで更新される
  • 参考になった を押すと数字が増え、「参考になった順」で順位が上がる
  • 営業時間と現在時刻から 営業中 / 本日休業 が出し分けられる
  • 絞り込みの条件チップが個別に解除できる
  • 実在の店舗名・チェーン名がデータに1つも含まれていない
  • フッターに「掲載内容はすべて架空」の明記がある

共通

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

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

資産 出所 本作での扱い
components/ui/ 8点 母艦SNS そのまま
components/shell/ 母艦SNS(+Market の maxWidth) そのまま
Price.jsx Market 未使用
Carousel.jsx Market ギャラリーに流用
Stepper.jsx Market 未使用
SmartImage.jsx Portal そのまま
fetch-images.mjs Portal そのまま
motion.js Portal コピー+追記
Stars / Distribution 系6点 本作 → 学習アプリの評価・進捗表示に転用可

5本目にして、新規実装が ui/ 6点と画面7枚まで縮みました。 母艦方式の投資回収がはっきり出ている状態です。


16. 補足:コスト感

シリーズ中で最も軽い1本になる見込みです。理由は3つ。

  1. 条件②③が資産の持ち込みで完結する(Session 0とSession 1の一部)
  2. 固有画面が実質2枚(R2・R3)で、残りは既存構造の流用
  3. グラフをライブラリではなく自前にしたことで、調整がmotion.jsの定数編集で済む

かかるのは Session 1(ui/ 6点)とSession 2(R2のシーケンス)に集中します。特に Stars.jsx の部分塗りは、SVGの linearGradient を使う指定を最初に渡してください。ここを曖昧にすると「星を4つ塗って5つ目を薄く」といった近似実装が出てきて、作り直しになります。§5.2に実装方針まで書いてあるので、そのまま渡せば1回で通ります。

FREE CONSULTATION

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

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

日程を決めて話す

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

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

まずは問い合わせる

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