社内SNS・社内ポータル テンプレート 要件定義書
プロダクト名:Souzoh Portal
シリーズ4本目。「デザインで殴る1本」として位置づけます。 前3本が題材の説明に軸足を置いていたのに対し、本アプリは開いた瞬間の印象を最優先の要件に置きます。 記事のサムネイル・OGP・営業資料に使う画像は、すべてこのアプリから撮る想定です。
1. 目的とゴール
| 項目 | 内容 |
|---|---|
| コンセプト | 全社のお知らせ・社内の動き・組織の情報が1画面に集まる、ダッシュボード型の社内ポータル |
| 一次目的 | 一目で「おっ」と思わせる。 URLを開いて2秒で判断される勝負 |
| 二次目的 | 前3本のC2Cと違いB2B(受注につながる題材)。読者=事業者が「自社にこれが欲しい」と直結する |
| 成功条件 | 記事のサムネイルにこの画面を使い、クリック率が他3本を上回る |
| 非ゴール | 実運用可能な社内システム。権限管理・監査ログ・SSO |
なぜこの題材が"すごく見える"のか:ポータルは数字・グラフ・人・お知らせ・写真が同時に載る唯一の題材です。前3本は一覧とカードしか出せませんでしたが、ここはKPI・組織図・カレンダー・ランキングを1画面に同居させられる。密度と多様性が、そのまま「作り込まれている感」になります。
2. 追加要件(今回の4条件)
本書では以下を独立した必達要件として扱い、それぞれ専用の章を設けています。
| 条件 | 対応章 | 実現方法の要旨 |
|---|---|---|
| ① デザインにとにかくこだわる | §5 | ベントーグリッド + ダークインク基調 + 可変フォント。§5.6にNGリストを明記 |
| ② ネットから自動で画像を取得しデモに組み込む | §6 | ビルド時に取得するNode製スクリプト(npm run fetch:images)+ 3層フォールバック |
| ③ 動きをつける | §7 | View Transitions API + スクロール連動 + 段差入場。14種類を定義 |
| ④ 一目ですごいと思わせる | §8 | 「最初の2秒」を専用に設計。ファーストビュー演出シーケンスを秒単位で規定 |
3. スコープ
3.1 作るもの
| # | 画面 | 優先度 | 母艦との関係 |
|---|---|---|---|
| P1 | ホーム(ベントーダッシュボード) | S | 完全新規。このアプリの本体 |
| P2 | 社内フィード(タイムライン) | A | 母艦S1を改修 |
| P3 | お知らせ一覧・詳細 | A | 母艦S1/S2を改修 |
| P4 | メンバー名鑑・組織図 | A | 新規 |
| P5 | メンバー詳細 | A | 母艦S5を改修 |
| P6 | 投稿作成(分報・称賛) | B | 母艦S6を改修 |
| P7 | 全社検索(⌘K パレット) | A | 新規。§8の"すごい"を担う要素 |
| P8 | メッセージ | C | 母艦S7をそのまま流用 |
3.2 作らないもの
- ❌ バックエンド・DB・認証・SSO・権限管理
- ❌ データ永続化(リロードで初期化)
- ❌ ファイルの実アップロード・実ダウンロード
- ❌ 実際のカレンダー連携・勤怠打刻の実処理
- ❌ ダークモード切替(ダーク固定。§5.2で理由を記述)
- ❌ 多言語、テストコード、CI
4. 技術構成
母艦を踏襲しつつ、このアプリのみ3点追加します。
Vite + React 18(JSX)
Tailwind CSS v4
lucide-react
────────── 以下このアプリのみ ──────────
motion(motion.dev)… レイアウトアニメーションのみに使用。§7.4に使用範囲を限定
recharts …………… KPIのスパークライン・面グラフ
cmdk ………………… ⌘Kコマンドパレット(P7)
方針転換の理由:前3本ではアニメーションライブラリを外しました。試行錯誤の往復が費用を食うためです。本アプリは要件③④が最優先なので判断を変えますが、代わりに§7で動きを14種類・数値付きで確定させています。ライブラリを入れても、仕様が確定していれば往復は増えません。増えるのは「もう少し滑らかに」を口頭で往復するときだけです。
recharts を入れる理由:KPIグラフを自前SVGで書くと、軸・目盛り・ツールチップの調整で必ず往復が出ます。ここは既製品が安いです。
5. デザイン仕様 ★要件①
5.1 方向性
シリーズ4本の視覚的な住み分けは次の通りです。
| アプリ | 基調 | 密度 | 主役 |
|---|---|---|---|
| SNS | 明るいウォームグレー | 低 | 文章 |
| Match | ディーププラム | 極低 | カード1枚 |
| Market | 白 | 高 | 価格 |
| Portal | ディープインク | 高+階層あり | 情報の集合そのもの |
Portalが狙うのは**「管制室」**の質感です。暗い地に、わずかに浮いたガラス質のパネルが不揃いなサイズで並び、そこにだけ光が入る。KPIの数字とライムの差し色が暗闇で映え、写真がパネルの中で発色する。
中心にあるのはベントーグリッドです。均一なカードの反復ではなく、大小・縦横比の違うタイルを組み合わせて1画面を構成します。これが「最新のアプリっぽさ」の正体で、なおかつスクリーンショット1枚に情報が凝縮されるので、記事の画像として最も強い。
5.2 ダーク固定にする判断
社内ポータルの実運用はライトが主流です。それでもダーク固定にします。
- 要件④(一目ですごい)に対して、暗背景 + 発光する差し色が最短距離であること
- 前3本がすべて明るい基調なので、4本並べたときの差が最大化されること
- 写真がダーク地では確実に映えること(要件②の効果が最大化される)
**ただしライト版はtheme.cssの値を差し替えるだけで出せる構造にします。**記事内で「トークンを差し替えるとこうなります」と両方見せられれば、テンプレートの価値の説明そのものになります。ライト版の値も§5.3に併記します。
5.3 デザイントークン(src/theme.css)
@theme {
/* ---- Base(ダーク) ---- */
--color-bg: #0B0D12; /* ほぼ黒。わずかに青 */
--color-surface: #131721; /* パネル面 */
--color-surface-2: #1B2130; /* パネル内の入れ子・入力欄 */
--color-surface-3: #232B3D; /* ホバー時 */
--color-ink: #EEF1F7; /* 主要テキスト */
--color-ink-sub: #8B93A7; /* メタ・ラベル */
--color-ink-dim: #5A6175; /* さらに弱い情報 */
--color-line: #262E3F; /* 1pxの区切り */
--color-line-lit: #38425C; /* パネル上端のハイライト線 */
/* ---- Accent ---- */
--color-accent: #C6F24E; /* ライム:CTA・現在地・強調 */
--color-accent-2: #4CC9F0; /* シアン:リンク・進行中 */
--color-accent-3: #F5A623; /* アンバー:期限・注意 */
--color-danger: #FF5C6C;
--color-ok: #3DDC97;
/* ---- Glass ---- */
--glass-bg: rgba(255,255,255,.035);
--glass-border: rgba(255,255,255,.075);
--glass-blur: 18px;
/* ---- Type ---- */
--font-display: "Space Grotesk", "Zen Kaku Gothic New", sans-serif; /* 600/700 */
--font-body: "Noto Sans JP", sans-serif; /* 400/500 */
--font-num: "JetBrains Mono", monospace; /* KPI・日付・時刻 */
--text-hero: clamp(32px, 4.2vw, 56px)/1.05; /* ★可変。PCで大きく効く */
--text-kpi: clamp(28px, 3.2vw, 44px)/1.0;
--text-title: 20px/1.35;
--text-body: 14px/1.8;
--text-label: 11px/1.3; /* 大文字+letter-spacing .12em で使う */
/* ---- Space ---- */
--space-1: 4px; --space-2: 8px; --space-3: 12px;
--space-4: 16px; --space-6: 24px; --space-8: 32px; --space-12: 48px;
/* ---- Shape ---- */
--radius-tile: 18px;
--radius-inner: 12px;
--radius-pill: 999px;
--shadow-tile: 0 1px 0 rgba(255,255,255,.05) inset,
0 20px 50px rgba(0,0,0,.45);
--shadow-lift: 0 30px 70px rgba(0,0,0,.6);
/* ---- Motion(§7で全て参照) ---- */
--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;
}
ライト版の差し替え値(記事用。この6行だけで切り替わる)
--color-bg:#F5F6F8; --color-surface:#FFFFFF; --color-surface-2:#EFF1F5;
--color-ink:#0E1219; --color-ink-sub:#5C6478; --color-line:#E3E6EC;
--color-accent:#5B39F5; /* ライムは白地で沈むのでインディゴに差し替え */
5.4 タイポグラフィの決め事
- 数字は必ず
--font-num+font-variant-numeric: tabular-nums。 KPIが並ぶ画面で桁がずれると一気に安っぽくなります - ラベル(
今週の動きなど)は--text-label+ 大文字/カタカナ +letter-spacing: .12em+--color-ink-sub - 見出しは
--font-displayの600以上、letter-spacing: -0.02em - 日本語本文の行間は1.8。ダーク地では明るい地より行間を広く取らないと詰まって見えます
5.5 パネル(Tile)の作り
全タイル共通の質感を1コンポーネントに閉じ込めます。
背景:--glass-bg + backdrop-filter: blur(var(--glass-blur))
枠 :1px solid --glass-border
上端:1pxの内側ハイライト(--shadow-tile のinset部分)
角 :--radius-tile
ホバー:translateY(-2px) + --shadow-lift + 枠が --color-line-lit へ(--dur-base)
backdrop-filterは必ず使ってください。 背景に極薄のグラデーション(後述)を敷いた上でガラスを重ねることで、平面的なダークUIとの差が出ます。ここが「最新っぽさ」の実体の半分です。
背景の下地:--color-bg の上に、右上から --color-accent を opacity .06 で、左下から --color-accent-2 を opacity .05 で、巨大な楕円グラデーションとして敷きます(filter: blur(120px))。スクロールしても動きません。これがガラスの下に薄く透ける。
5.6 やらないこと(デザイン品質のためのNGリスト)
- ❌ 紫→ピンクのグラデーションボタン(2020年代前半の記号。古く見えます)
- ❌ 全タイルへの均一な影(§5.5のホバー時のみ)
- ❌ 絵文字をUIアイコンとして使う(lucide-reactに統一)
- ❌ 角丸の混在(
--radius-tile/--radius-inner/--radius-pillの3種のみ) - ❌ 中央揃えの長文(日本語の左揃えを崩さない)
- ❌ 3種を超えるフォント
- ❌ ネオン発光の多用(
--color-accentのbox-shadow発光はKPIタイル1枚とライブ表示のみ)
6. 画像の自動取得 ★要件②
6.1 方針:ランタイム取得ではなく「ビルド時取得」
「ネットから自動で画像を持ってくる」の実装には2通りありますが、ビルド時に取得してローカルに置く方式を採ります。
ランタイム取得(<img src="https://外部...">) |
ビルド時取得(採用) | |
|---|---|---|
| 実装量 | 少ない | スクリプト1本(約80行) |
| 表示速度 | 毎回外部リクエスト。遅い・ちらつく | 自ドメイン配信。速い |
| サービス停止時 | デモが全部壊れる | 影響なし |
| レート制限 | 記事がバズると詰まる | 影響なし |
| 再現性 | 開くたびに絵が変わる | seedで固定 |
配布テンプレートで外部サービスに毎回問い合わせる作りにすると、そのサービスが止まった日に全記事のデモが同時に壊れます。実際、長年使われてきた via.placeholder.com は数か月にわたって断続的にダウンし、告知も移行先の案内もないまま404や接続リセット、DNSタイムアウトを繰り返すようになりました。同様に source.unsplash.com も廃止され、公式APIで同じことをする手段は用意されませんでした。この構造的リスクを、記事5本分背負う必要はありません。
6.2 取得元
picsum.photos を使います。実在の写真を指定サイズで返す定番のプレースホルダサービスで、seedを指定すれば同じ画像を安定して取得できます。ただし本番の実行時依存にするのではなく、公開前にキャッシュか自前ホストへ移すのが前提の使い方です。picsum.photos と placehold.co は2026年時点で稼働・保守されていますが、それでも外部依存であることに変わりはないので、§6.1の方針で吸収します。
https://picsum.photos/seed/{seed}/{w}/{h}
seedを固定しているので、何度実行しても同じ絵が返ります。これが再現性の担保になります。
顔写真は取得しません。 社内SNSにはメンバーの顔が要りますが、実在の人物の写真を架空の社員として配布テンプレートに載せるのは肖像の扱いとして避けるべきです。メンバーのアバターは母艦のID由来グラデーション+頭文字方式を使います(§6.5)。ダーク地では、この幾何学的なアバターのほうがむしろ洗練されて見えます。
6.3 スクリプト仕様(scripts/fetch-images.mjs)
npm run fetch:images
src/data/imageManifest.jsを読む(seed・幅・高さ・用途の一覧)- 各エントリを
https://picsum.photos/seed/{seed}/{w}/{h}から取得 public/images/{seed}.jpgに保存- 既に存在するファイルはスキップ(
--forceで上書き) - 取得失敗は握りつぶし、ログに1行出して次へ進む(1枚の失敗でビルドを止めない)
- 完了後にサマリを出力:
取得 18 / スキップ 4 / 失敗 0
同時実行は4本までに絞ってください(Promise を4本ずつ処理)。無制限に並列化すると相手側に負荷をかけますし、レート制限にも当たります。
public/images/ は .gitignore に入れます。 取得した写真をリポジトリにコミットして再配布すると、ライセンスの筋が曖昧になります。配るのはスクリプトとマニフェストだけ。利用者が自分で npm run fetch:images を叩く形にすれば、権利関係はその人と取得元の間で完結します。README冒頭にこの手順を書いてください。
6.4 マニフェスト(src/data/imageManifest.js)
export default [
// ヒーロー・特集
{ seed:"portal-hero", w:1600, h:900, use:"P1ヒーロー" },
{ seed:"news-offsite", w:1200, h:800, use:"お知らせ:全社合宿" },
{ seed:"news-office", w:1200, h:800, use:"お知らせ:新オフィス" },
{ seed:"news-award", w:1200, h:800, use:"お知らせ:表彰" },
// フィード投稿
{ seed:"feed-desk", w:1000, h:750, use:"分報:作業風景" },
{ seed:"feed-whiteboard", w:1000, h:750, use:"分報:議論" },
{ seed:"feed-lunch", w:1000, h:1000,use:"分報:ランチ" },
{ seed:"feed-launch", w:1000, h:562, use:"分報:リリース" },
{ seed:"feed-training", w:1000, h:750, use:"分報:研修" },
{ seed:"feed-site", w:1000, h:562, use:"分報:現場" },
// 部署カバー
{ seed:"team-dev", w:800, h:400, use:"開発部" },
{ seed:"team-sales",w:800, h:400, use:"営業部" },
{ seed:"team-cs", w:800, h:400, use:"CS部" },
{ seed:"team-corp", w:800, h:400, use:"管理部" },
// イベント
{ seed:"event-allhands", w:900, h:600, use:"全社会議" },
{ seed:"event-study", w:900, h:600, use:"勉強会" },
{ seed:"event-welcome", w:900, h:600, use:"歓迎会" },
{ seed:"event-1on1", w:900, h:600, use:"1on1週間" },
];
18枚です。この規模なら取得は数秒で終わります。
6.5 表示側:3層フォールバック(ui/SmartImage.jsx)
絶対に壊れた画像アイコンを出さないための構造です。
第1層:/images/{seed}.jpg ← ビルド時に取得済み。通常はここ
↓ onError
第2層:https://picsum.photos/seed/{seed}/{w}/{h} ← 取得し忘れた場合の保険
↓ onError
第3層:seed由来の決定的グラデーション(母艦 GradientImage) ← 必ず何かは映る
さらに全層共通で:
- 読み込み完了までseed由来の2色グラデーションを敷き、完了時に
opacity 0→1(--dur-slow)でクロスフェード。白い箱がパッと出る現象を消します width/height属性を必ず指定してレイアウトシフトを防ぐ- ファーストビュー外は
loading="lazy"+decoding="async"
このコンポーネントは前3本にも後付けできます。母艦へ還元してください。
7. 動き ★要件③
7.1 原則
- 意味のない動きは入れない。 出現・状態変化・因果の3つだけを表現する
- 主要な動きは
--dur-base(240ms)、退場は入場の6割の速さ、大きな移動のみ--dur-slow(520ms) - イージングは
--ease-outを基本とし、往復する動きだけ--ease-inout prefers-reduced-motion: reduceで下表の「停止」列がすべて止まること(受け入れ基準)
7.2 動きの一覧(14種)
| # | 名称 | 対象 | 仕様 | reduced時 |
|---|---|---|---|---|
| 1 | 段差入場 | P1の全タイル | 下から16px+opacity 0→1、--dur-slow、60msずつ遅延(最大12枚) |
停止(即表示) |
| 2 | KPIカウントアップ | KPI4枚の数字 | 0→目標値、900ms、--ease-out。桁はtabular-numsで固定 |
停止(即表示) |
| 3 | スパークライン描画 | KPIタイル | SVGパスをstroke-dasharrayで左から1.1秒描画 |
停止(完成形) |
| 4 | スクロール連動リビール | P1下部・P2 | 画面下20%に入ったら 下から24px+fade、--dur-slow |
停止 |
| 5 | タイルのホバー浮上 | 全タイル | translateY(-2px)+影強化+枠色、--dur-base |
影のみ |
| 6 | 画面遷移 | 全ページ | View Transitions APIでクロスフェード+4px上移動、--dur-base |
即時切替 |
| 7 | 共有要素モーフ | メンバー→詳細 | view-transition-nameでアバターと氏名が連続移動 |
停止 |
| 8 | ⌘Kパレット | P7 | 背景 blur(0→10px)+パネルがscale .96→1、--dur-base |
フェードのみ |
| 9 | 検索結果の入れ替え | P7 | 各行20ms差でスライドイン。入力中はデバウンス120ms | 停止 |
| 10 | 称賛(拍手)演出 | フィード | ボタン押下でライムの粒が8個放射、600msで消滅+数字がスライド | 数字のみ変化 |
| 11 | ライブ在席パルス | 在席中バッジ | 緑ドットが2.4秒周期でゆっくり明滅 | 停止 |
| 12 | 組織図の展開 | P4 | 部署をクリックでメンバーが高さアニメ+段差入場(40ms差) | 即展開 |
| 13 | 進捗リング | P1「今週の目標」 | 円弧をstroke-dashoffsetで0→達成率、1.2秒 |
完成形 |
| 14 | トースト | 全体 | 右下から16px上+fade、3秒後に自動退場 | フェードのみ |
7.3 ファーストビューの演出シーケンス ★最重要
開いてから2.0秒で完結させます。長いローディング演出は逆効果です。
0ms 背景グラデーションのみ表示(ほぼ黒)
120ms ヘッダーとサイドナビが fade-in
200ms ヒーロータイル(大)が下から入場
260ms KPIタイル4枚が 60ms差で順に入場 ← #1
400ms KPIの数字が一斉にカウントアップ開始 ← #2
同時にスパークラインが左から描画 ← #3
900ms 残りのタイルが 60ms差で入場
1400ms 全タイル着地
2000ms 完了(以降は静止。パルス#11のみ継続)
カウントアップとスパークラインを同時に走らせるのが要点です。数字と線が同時に動く0.9秒が、このアプリで最も"すごい"瞬間になります。ここは実装で必ず確認してください。
7.4 motion ライブラリの使用範囲(限定)
無制限に使うと往復が増えます。以下4つ以外はCSSで実装してください。
- ✅ #7 共有要素モーフ(
layoutId) - ✅ #12 組織図の高さアニメーション
- ✅ #8 パレットの出入り
- ✅ #10 拍手の粒
- ❌ 上記以外(#1〜#6, #9, #11, #13, #14 はCSS / WAAPI / View Transitions で書く)
8. 「一目ですごい」の設計 ★要件④
8.1 最初の2秒で伝えるもの
読者はスクロールしません。ファーストビュー1枚で判断します。そこに以下が同時に載っている状態を作ります。
- 大きな数字(KPI)が動いている
- 写真が発色している(暗い地に写真が浮く)
- 情報の種類が多い(数字・グラフ・人・写真・お知らせ・カレンダー)
- タイルの大きさが不揃い(=テンプレ感の否定)
- 右上にライブ表示(
在席 12名の緑パルス)
8.2 ベントーグリッドの構成(P1・PC 1440px時)
12カラムグリッド。行の高さは140px単位。
┌─────────────────────────┬───────────┬───────────┐
│ ヒーロー:今週のハイライト │ KPI① │ KPI② │
│ (写真+見出し) 6col × 2row │ 3col×1row │ 3col×1row │
│ ├───────────┼───────────┤
│ │ KPI③ │ KPI④ │
├─────────────┬───────────────────┴───────────┴───────────┤
│ 今週の目標 │ 社内フィード(直近5件・縦スクロール) │
│ 進捗リング │ 8col × 3row │
│ 4col × 3row │ │
├─────────────┼─────────────────┬─────────────────────────┤
│ 在席メンバー │ 今週の予定 │ 称賛ランキング │
│ 4col × 2row │ 4col × 2row │ 4col × 2row │
└─────────────┴─────────────────┴─────────────────────────┘
- タブレット(769〜1279px):6カラムに再構成。ヒーローは6col、KPIは3col×2段
- モバイル(〜768px):1カラム縦積み。KPIのみ2列。下部固定タブバー
★KPIを2×2の塊にしてヒーローの右に置くのが要点です。均等な4枚横並びは「ダッシュボードのテンプレ」に見えますが、塊にすると意図のあるレイアウトに見えます。
8.3 ⌘K コマンドパレット(P7)
要件④への貢献度が最も高い機能です。実装量に対して印象が跳ね上がります。
⌘K/Ctrl+Kで起動。ヘッダーにも⌘Kの表示を出す- 横断検索:メンバー・お知らせ・投稿・部署・申請メニュー
- 結果はカテゴリごとにグルーピングし、種別アイコン付き
↑↓で移動、Enterで遷移、Escで閉じる(キーボードだけで完結すること)- 入力が空のときは「最近見たもの」5件と「クイックアクション」3件を表示
- 検索語が0件のときは「"◯◯" は見つかりませんでした」+近い候補を提示
8.4 記事用スクリーンショットの撮り方(運用メモ)
- 撮影は 1440×900 のPC幅、ファーストビュー着地後(2.0秒経過後)
- ⌘Kを開いた状態のカットを必ず1枚押さえる(記事中盤の画像に最適)
- モバイル幅は390pxで2枚(ホーム・フィード)
- 前3本と並べるOGP用に、ライト版に切り替えたカットも1枚撮っておく
9. 画面仕様
P1. ホーム(ベントーダッシュボード)★本体
ヒーロータイル:portal-hero の写真を背景に、下から --color-bg へのグラデーションを重ね、その上に今週のハイライト見出し+日付+詳しく見る。
KPIタイル4枚
| タイル | 数字 | 補助 |
|---|---|---|
| 今月の受注 | ¥ 8,420,000 |
前月比 +12.4%(--color-ok)+ スパークライン |
| 稼働プロジェクト | 17 件 |
内 3 件が納期3日以内(--color-accent-3) |
| 新規問い合わせ | 48 件 |
今週 +9(スパークライン) |
| 平均残業 | 11.2 h |
前月比 −2.8h(--color-ok) |
数字は --font-num + --text-kpi。「今月の受注」タイルのみ --color-accent の淡い発光を許可(§5.6の例外)。
今週の目標:進捗リング(#13)で 68%。中央に大きく数字、下に 目標 25件 / 実績 17件。
社内フィード:直近5件をコンパクト表示。タイル内で縦スクロール。すべて見る でP2へ。
在席メンバー:アバターを敷き詰め、在席中は緑パルス(#11)、離席は opacity .45。12 / 28名 の表示。
今週の予定:日付ごとに3件。今日の行は --color-accent の縦線でマーク。
称賛ランキング:今月の称賛獲得数トップ5。1位のみ数字を大きく。
P2. 社内フィード
- タブ:
全社/自部署/フォロー中 - 投稿種別を色付きラベルで区別:
分報(シアン)/称賛(ライム)/お知らせ(アンバー)/質問(グレー) - 称賛投稿は宛先メンバーのアバターを本文上に表示
- 拍手ボタン(#10)。リアクション絵文字は使わず、拍手1種に統一(§5.6のNGに沿う)
- スクロール連動リビール(#4)
P3. お知らせ一覧・詳細
- 一覧:重要度で3段階(
必読は左端にライムのバー + 未読バッジ) - カテゴリ:
人事総務システムイベント全社 - 詳細:カバー写真 + 本文 + 添付ファイル風のリスト(クリックでトースト)+
既読にする - 既読にすると一覧のバッジが消え、P1の未読カウントも連動
P4. メンバー名鑑・組織図
- 切替タブ:
名鑑/組織図 - 名鑑:部署フィルタ + 検索。カードグリッド(アバター・氏名・部署・役職・在席状態・スキルタグ)
- 組織図:部署をクリックで展開(#12)。部署カバー写真を薄く背景に敷く
- カードクリック → P5(共有要素モーフ #7)
P5. メンバー詳細
- ヘッダー:部署カバー写真 + アバター(モーフ着地点)
- 基本情報:部署・役職・入社年月・内線・在席状態
できることスキルタグ- 直近の投稿3件・獲得した称賛3件
メッセージを送る(P8へ)
P6. 投稿作成
- ボトムシート。種別を先に選ぶ:
分報/称賛/質問 - 称賛を選ぶと宛先メンバーの選択UIが出る(名鑑から複数選択可)
- 本文500字+画像ピッカー(マニフェストの画像から選択)
- 投稿 → P2先頭に挿入 + P1のフィードにも反映 + トースト
P7. ⌘Kコマンドパレット
§8.3の通り。
P8. メッセージ
母艦S7をそのまま流用。送信1.2秒後に定型返信。
10. データモデル
// member
{ id:"m01", name:"西澤 志門", kana:"にしざわ しもん",
dept:"経営", role:"代表", joinedAt:"2019-04",
ext:"101", present:true,
skills:["事業開発","ノーコード","AI活用"],
avatarSeed:"m01" } // ★写真ではなくグラデ生成(§6.2)
// post
{ id:"f01", type:"log"|"praise"|"question"|"notice",
memberId:"m04", body:"...", createdAt:"...",
toMemberIds:["m07"], // praise のみ
image:{ seed:"feed-desk", w:1000, h:750 } | null,
claps:14, clapped:false, comments:[...] }
// notice
{ id:"n01", category:"人事"|"総務"|"システム"|"イベント"|"全社",
priority:"must"|"normal", title:"...", body:"...",
image:{ seed:"news-offsite", w:1200, h:800 },
publishedAt:"...", read:false, attachments:["就業規則_2026版.pdf"] }
// kpi
{ key:"revenue", label:"今月の受注", value:8420000, unit:"円",
delta:+12.4, series:[6.1,6.8,7.2,7.0,7.9,8.4] }
// event
{ id:"e01", date:"2026-07-24", time:"10:00",
title:"全社定例", place:"大会議室 / オンライン" }
11. デモデータ
11.1 メンバー(28名/主要12名を明記)
| id | 氏名 | 部署 | 役職 | 在席 | スキル |
|---|---|---|---|---|---|
| m00 | あなた | 開発 | メンバー | ✓ | — |
| m01 | 西澤 志門 | 経営 | 代表 | ✓ | 事業開発 / ノーコード / AI活用 |
| m02 | 菊池 星哉 | 開発 | テックリード | ✓ | アーキテクチャ / React / 要件定義 |
| m03 | 海野 遥 | 開発 | エンジニア | — | 実装 / API連携 / 自動化 |
| m04 | 高木 芽衣 | 営業 | マネージャー | ✓ | 提案 / 折衝 / 与件整理 |
| m05 | 相馬 隼人 | 営業 | メンバー | ✓ | 新規開拓 / 商談 |
| m06 | 三宅 千秋 | CS | リーダー | ✓ | 導入支援 / 研修設計 |
| m07 | 大野 湊斗 | CS | メンバー | — | 問い合わせ対応 / FAQ整備 |
| m08 | 若林 詩織 | 管理 | 人事 | ✓ | 採用 / 労務 / 制度設計 |
| m09 | 堀内 樹 | 管理 | 経理 | ✓ | 経理 / 資金繰り |
| m10 | 綾部 実咲 | 開発 | デザイナー | ✓ | UI設計 / 情報設計 |
| m11 | 郡司 蓮 | 営業 | メンバー | — | インサイドセールス |
残り16名は氏名・部署・役職のみで生成(在席は約4割をtrue)。在席は12名に調整してください(P1の 12 / 28名 と一致させる)。
11.2 KPI
| key | ラベル | 値 | 前月比 | series |
|---|---|---|---|---|
| revenue | 今月の受注 | 8,420,000 | +12.4% | 6.1 / 6.8 / 7.2 / 7.0 / 7.9 / 8.4 |
| projects | 稼働プロジェクト | 17 | +2 | 12 / 13 / 15 / 14 / 16 / 17 |
| inquiries | 新規問い合わせ | 48 | +9 | 31 / 35 / 33 / 40 / 39 / 48 |
| overtime | 平均残業 | 11.2h | −2.8h | 18.4 / 17.1 / 15.9 / 14.2 / 14.0 / 11.2 |
11.3 お知らせ(7件)
| 重要度 | カテゴリ | タイトル | 画像 |
|---|---|---|---|
| 必読 | 人事 | 2026年度下期の目標設定について(8月8日締切) | — |
| 必読 | システム | 経費精算システムの切り替えに伴う操作説明 | — |
| 通常 | イベント | 全社合宿を10月17〜18日に開催します | news-offsite |
| 通常 | 総務 | 新オフィス(蔵前)への移転が完了しました | news-office |
| 通常 | 全社 | 上期MVPを表彰しました | news-award |
| 通常 | 総務 | 夏季休暇の取得ルールについて | — |
| 通常 | システム | 社内Wikiの構成を見直しました | — |
11.4 フィード投稿(12件)
| 種別 | 投稿者 | 本文 | 画像 | 拍手 |
|---|---|---|---|---|
| 称賛 | m04 | 高田工業所の追加要件、m02さんが休日に整理してくれたおかげで月曜の打ち合わせが3分で終わりました | — | 47 |
| 分報 | m02 | 朝から詰まっていた同期処理、原因はこちらの設定漏れでした。手順書に追記しておきます | feed-desk | 12 |
| 分報 | m10 | 新しいポータルの配色、暗い地に振ってみました。写真がちゃんと発色するのが良い | feed-whiteboard | 31 |
| 称賛 | m06 | 導入研修の資料、m07さんが受講者の質問を全部拾って改訂してくれました | — | 38 |
| 分報 | m05 | 新規のお問い合わせが今週9件。年明け以来のペースです | — | 24 |
| 質問 | m03 | Cloudflare Workersの環境変数、本番と検証で分ける運用ってどうしてますか | — | 6 |
| 分報 | m08 | 来月の入社が2名決まりました。受け入れ準備を進めます | feed-training | 55 |
| 分報 | m09 | 上期の締めが見えてきました。経費精算は今週中にお願いします | — | 9 |
| 分報 | m01 | 新オフィス、思ったより静かで集中できます。窓際の席が人気 | feed-lunch | 41 |
| 分報 | m02 | リリース完了しました。今回は差し戻しゼロ | feed-launch | 62 |
| 称賛 | m01 | 現場対応でm03さんが即日で駆けつけてくれました。助かりました | feed-site | 33 |
| 質問 | m11 | 商談メモのテンプレート、みなさん何を使っていますか | — | 4 |
11.5 今週の予定(8件)
| 日付 | 時刻 | 予定 | 場所 |
|---|---|---|---|
| 7/23(木) | 10:00 | 全社定例 | 大会議室 / オンライン |
| 7/23(木) | 15:00 | 高田工業所 定例 | オンライン |
| 7/24(金) | 11:00 | 新規商談(製造業) | 先方オフィス |
| 7/24(金) | 17:30 | 開発ふりかえり | 開発エリア |
| 7/27(月) | 09:30 | 週次キックオフ | 大会議室 |
| 7/28(火) | 14:00 | 社内勉強会「AI活用の現在地」 | セミナールーム |
| 7/29(水) | 13:00 | 1on1 週間(〜7/31) | 各所 |
| 7/30(木) | 18:30 | 歓迎会 | 蔵前 |
11.6 称賛ランキング(今月)
| 順位 | メンバー | 獲得数 |
|---|---|---|
| 1 | m02 菊池 星哉 | 34 |
| 2 | m06 三宅 千秋 | 28 |
| 3 | m03 海野 遥 | 21 |
| 4 | m08 若林 詩織 | 19 |
| 5 | m04 高木 芽衣 | 16 |
12. ディレクトリ構成
souzoh-portal/
├── scripts/
│ └── fetch-images.mjs ★新規:§6.3
├── public/images/ ← .gitignore対象。取得先
├── src/
│ ├── theme.css ★差し替え(§5.3)
│ ├── app.config.js ★差し替え(下記)
│ ├── data/
│ │ ├── members.js posts.js notices.js kpis.js
│ │ ├── events.js imageManifest.js
│ ├── components/ui/ ← 母艦からコピー
│ │ ├── SmartImage.jsx ★新規:3層フォールバック(§6.5)→ 母艦へ還元
│ │ ├── Tile.jsx ★新規:ガラスパネル(§5.5)
│ │ ├── CountUp.jsx ← 母艦から流用
│ │ ├── ProgressRing.jsx ★新規
│ │ └── Sparkline.jsx ★新規(recharts薄ラッパ)
│ ├── components/shell/ ← 母艦からコピー(layout.maxWidth 対応済み)
│ ├── features/
│ │ ├── home/ ★新規:ベントーグリッド
│ │ ├── feed/ notices/ directory/ member/ composer/
│ │ ├── palette/ ★新規:⌘K
│ │ └── chat/ ← 母艦 dm/ をそのまま
│ ├── motion.js ★新規:#1〜#14の定数を1箇所に
│ ├── store.js
│ └── main.jsx
└── README.md
// app.config.js
export default {
appName: "Souzoh Portal",
tagline: "会社の「いま」が、ひと目で",
layout: { maxWidth: 1440, bento: true },
nav: [
{ key:"home", label:"ホーム", icon:"LayoutDashboard" },
{ key:"feed", label:"フィード", icon:"MessageSquare" },
{ key:"notices", label:"お知らせ", icon:"Megaphone" },
{ key:"directory", label:"メンバー", icon:"Users" },
{ key:"me", label:"マイページ", icon:"User" },
],
labels: {
listTitle: "社内の動き",
itemNoun: "投稿",
createCta: "投稿する",
primaryAction: "拍手",
},
};
13. 実装手順(Claude Codeへの投げ方)
5分割。 Market同様に重い1本ですが、重さの理由が違います(固有画面数ではなく、演出の作り込み)。
| Session | やること | 冒頭で読ませるもの |
|---|---|---|
| 0 | 母艦を複製 → theme.css app.config.js data/ 差し替え。画面は触らずビルドを通す |
§5.3・§11・§12 |
| 1 | scripts/fetch-images.mjs + SmartImage + Tile + motion.js。画像18枚の取得までを完了させる |
§6全体・§5.5・§7.1 |
| 2 | P1ホーム(ベントーグリッド+KPI+演出シーケンス) | §5・§7.2(#1〜5,13)・§7.3・§8.2・§9(P1) |
| 3 | P7 ⌘Kパレット + 画面遷移(View Transitions) + 共有要素モーフ | §7.2(#6,7,8,9)・§8.3 |
| 4 | P2フィード + P3お知らせ + P6投稿作成 | §9(P2,P3,P6)・§7.2(#4,10) |
| 5 | P4名鑑・組織図 + P5メンバー詳細 + 仕上げ(reduced-motion全確認) | §9(P4,P5)・§7.2(#11,12)・§14 |
Session 1を必ず独立させてください。 画像が18枚ローカルに揃った状態でP1に入ると、レイアウト確認が1発で済みます。画像なしでグリッドを組むと、写真が入った瞬間に余白バランスの作り直しが発生します。要件②を先に片付けることが、要件①のコストを下げます。
Session 2 の指示文テンプレ:
添付の要件定義書 §5・§7.3・§8.2・§9(P1) を実装してください。
src/components/ui/ の Tile / SmartImage / CountUp と src/motion.js は完成済みです。
新規作成せず import して使ってください。
ベントーグリッドは CSS Grid の grid-template-areas で組み、
§8.2 の配置図をそのまま再現してください。
アニメーションの遅延・時間は src/motion.js の定数のみを参照し、
JSX に数値を直接書かないでください。
色・余白・角丸は src/theme.css の変数のみを使ってください。
実装前に、作成・変更するファイルの一覧だけ先に提示してください。
14. 受け入れ基準
要件①(デザイン)
- 生の色コード・生のpx値がJSXに1つも無い(全てトークン経由)
- 角丸が3種類のみ、フォントが3種類のみ
- タイルに
backdrop-filterが効き、背景の淡いグラデーションが透けている - KPIの数字が
tabular-numsで桁揃えされている - §5.6のNGリストに1つも該当しない
要件②(画像)
-
npm run fetch:imagesで18枚がpublic/images/に取得される - 2回目の実行でスキップされ、
--forceで再取得される -
public/images/を空にしても壊れた画像アイコンが1つも出ない(第2層・第3層が働く) - 画像読み込み時にレイアウトシフトが起きない
-
public/images/が.gitignoreに入っている - メンバーのアバターに実在人物の写真が使われていない
要件③(動き)
- §7.2の14種すべてが動作する
-
prefers-reduced-motion: reduceで§7.2の「停止」列がすべて止まる - ⌘K がキーボードのみで完結する(起動・移動・決定・閉じる)
- View Transitions 非対応ブラウザでも遷移が壊れない(即時切替にフォールバック)
要件④(第一印象)
- 初回表示が§7.3のシーケンス通りに進み、2.0秒で着地する
- カウントアップとスパークライン描画が同時に走る
- ファーストビューに「数字・グラフ・人・写真・お知らせ・カレンダー」が同時に見える
- タイルの大きさが不揃いで、均一グリッドに見えない
共通
- 390px / 1024px / 1440px の3幅で崩れない
-
npm run buildが警告なく通り、Cloudflare Pagesで表示される - ライト版トークンに差し替えても破綻しない(記事用カット撮影のため)
15. 母艦テンプレへの還元
このアプリで作った以下は、前3本にも後付けする価値があります。
| 成果物 | 還元先 |
|---|---|
SmartImage.jsx(3層フォールバック) |
全アプリ。特にMarketの商品写真が実写になる |
scripts/fetch-images.mjs |
全アプリ。Marketの「実写推奨」がスクリプト一発で解決する |
motion.js(動きの定数化) |
全アプリ |
| View Transitions による画面遷移 | 全アプリ。既存のフェード実装と置き換え可能 |
特に
fetch-images.mjsは Market の課題を直接解決します。 あちらの§4.4で「実写推奨」と書きながら手作業を前提にしていましたが、マニフェストを書いてスクリプトを叩くだけになります。Portalを先に作り、Marketに戻って適用するのが最も効率的です。
16. 補足:コスト感
シリーズ中でMarketと並んで重い1本ですが、重さの質が違います。Marketは状態遷移の複雑さ、Portalは演出の作り込みです。
演出は「もう少し滑らかに」「もう少し速く」の口頭往復が最も費用を食う領域なので、本書では§7.2で14種すべてに数値を与え、§7.3でシーケンスをミリ秒単位で確定させています。motion.js に定数を集約させる指示も同じ目的です。感覚的な指示を一切挟まずに済む状態を作ってあるので、そのまま渡してください。
調整が必要になった場合も、motion.js の数値を1箇所書き換えるだけで全体に反映されます。再生成ではなく定数の編集で済む構造にしてあることが、このアプリで最大のコスト対策です。