概要

項目内容
案件 IDMAS-356
案件名Tremor + v0.dev 採用(フロントエンド UI コンポーネント生成効率化)
カテゴリ🎨 UI / フロントエンド技術基盤
優先度P2 ★★
推奨実装順MAS-356 → MAS-358 → MAS-357(本案件が MAS-358 左サイドバー実装の前提)
関連案件MAS-355(CapitalAllocationPanel が Phase 2 の試験ターゲット)/ MAS-358(本案件の Tremor 基盤を活用した左サイドバー実装)/ MAS-357(Firebase 移行・本案件完了後に実施)
新規追加ファイルwebapp_client/tailwind.config.ts(Phase 1)/ webapp_client/src/styles/tremor-scope.css(Phase 1)
変更ファイルwebapp_client/vite.config.ts(Tailwind v4 プラグイン追加)/ webapp_client/package.json(依存追加)/ webapp_client/src/cockpit/CapitalAllocationPanel.tsx(Phase 2 試験移行)
実装ステータス完了

背景・目的

現状の課題

bizlp コックピットのフロントエンドは React + Vite + カスタム CSS で構成されている。

課題定量影響
UI コンポーネントのスクラッチ実装コストKPI カード 1 枚に HTML + CSS + TypeScript で 50〜100 行。cockpit.css が 2,673 行に肥大化
ゲージ / スピードメーター / プログレスバーの自作Rule of 40 スコア・ROE 等の視覚的指標を都度 SVG / div で実装。再利用性低
パネル新規追加の参入障壁Jr エンジニア (MAS-230) が担当できる難易度まで下げるために、コンポーネント生成の自動化が必要
v0.dev 産物の活用不可Vercel v0.app が出力する React + Tailwind + shadcn/ui コードは、Tailwind 未導入の現スタックでは動作しない

解決策

Tailwind CSS v4 + Tremor v3 (@tremor/react) をダッシュボード専用 UI 基盤として導入し、v0.dev をコード生成ワークフローに組み込む。

現状: カスタム CSS (2,673 行) + React JSX 手書き
導入後: Tailwind ユーティリティ + Tremor コンポーネント + v0.dev で生成加速

Tremor / v0.dev の位置づけ

ツール役割採用形態
Tailwind CSS v4ユーティリティクラス + Tremor / shadcn の前提条件npm 依存 (@tailwindcss/vite)
Tremor v3ダッシュボード専用コンポーネント (KPI カード / ゲージ / バー / テーブル)npm 依存 (@tremor/react)
v0.devプロンプト → React + Tailwind + shadcn/ui コード生成外部 Web ツール (依存なし・コード貼り付け)

技術スタックの変更

現状

React 18 + Vite 5 + TypeScript
vite-plugin-singlefile (全 JS/CSS を 1 HTML にインライン化)
カスタム CSS のみ (cockpit.css / sidebar.css)
chart.js + d3-sankey (チャート)

導入後

React 18 + Vite 5 + TypeScript              ← 変更なし
vite-plugin-singlefile                       ← 変更なし (制約:後述)
Tailwind CSS v4 (@tailwindcss/vite)          ← 新規追加
Tremor v3 (@tremor/react)                    ← 新規追加 (ダッシュボード用)
カスタム CSS (既存パネルはそのまま維持)      ← 共存
chart.js + d3-sankey                         ← 変更なし

重要制約と事前検証項目

制約 1: vite-plugin-singlefile によるインライン化

GAS HtmlService の CSP 制約上、vite-plugin-singlefile で全 JS/CSS を 1 HTML にインライン化している。Tailwind と Tremor の追加はバンドルサイズに直接影響する。

ファイル現状サイズ目標上限
financial_cockpit.html364 KB600 KB 以内
multiyear_cockpit.html290 KB500 KB 以内
sidebar_spa_shell.html179 KB350 KB 以内

GAS HtmlService のファイルサイズ上限は公式ドキュメント未記載だが、実績上 数 MB は問題ない。ただしブラウザロード時間は GAS Web App の ?embedded=1 iframe 内で体感するため、目標上限を設ける。

Tailwind v4 の PurgeCSS 相当機能 (@tailwindcss/vite の tree-shaking) により、使用していないユーティリティクラスは最終バンドルに含まれない。Phase 2 完了後にビルドサイズを実測してから続行判断する(ゴーサイン判定)。

制約 2: 既存 cockpit.css との共存

cockpit.css (2,673 行) は既存パネル全体の見た目を定義している。Tailwind を導入すると CSS Reset (preflight) が既存スタイルと衝突する可能性がある。

対処方針: @layer base { ... } で Tailwind の preflight を Tremor 専用スコープに閉じ込める。具体的には tremor-scope.css@scope .tremor-root { @tailwind base; ... } として既存 CSS に影響させない。

制約 3: Tremor v3 の安定性

Tremor は v2 が安定版・v3 が shadcn/ui ベースへの移行版(2025 年時点で開発中)。Phase 2 着手前に @tremor/react の最新安定版をピン留めし、CHANGELOG を確認すること。 v3 の API が破壊的変更を含む場合は v2 (@tremor/[email protected]) で代替する。

バージョン状態備考
v2 (@tremor/[email protected])安定版Tailwind v3 が前提
v3 (@tremor/[email protected])移行版Tailwind v4 + shadcn/ui ベース。着手前に安定性確認必須

実装仕様

Phase 1 — Tailwind CSS v4 導入

目的: Tremor と v0.dev 産物コードの両方の前提条件を整える。

1-A. 依存追加

cd webapp_client
npm install --save-dev tailwindcss @tailwindcss/vite
# Tremor が v2 なら: npm install @tremor/react tailwindcss@3  (v3 なら上記のみ)

1-B. vite.config.ts 変更

import tailwindcss from '@tailwindcss/vite';        // 追加

export default defineConfig({
  plugins: [
    react(),
    tailwindcss(),   // ← react() の後に追加
    viteSingleFile(),
  ],
  // ... 既存設定は変更なし
});

1-C. tremor-scope.css — Tailwind を Tremor 専用スコープに閉じ込める

ファイル: webapp_client/src/styles/tremor-scope.css

/*
 * MAS-356: Tremor コンポーネントにのみ Tailwind を適用するスコープ境界。
 * .tremor-root クラスを持つ要素の内側にのみ Tailwind の preflight と
 * ユーティリティが適用され、既存 cockpit.css との衝突を防ぐ。
 */
@import "tailwindcss" layer(tremor);

@layer tremor.base {
  /* Tailwind preflight をここに閉じ込める (既存 CSS に影響させない) */
}

既存の cockpit.css の先頭に @import './tremor-scope.css'; を追加する(cockpit / multiyear ビルドのみ)。

1-D. 動作確認

npm run build:cockpit が成功し、financial_cockpit.html のサイズが大幅増加していないことを確認する(Tailwind のみでは PurgeCSS が効くため数 KB 増程度が期待値)。


Phase 2 — Tremor 試験採用(CapitalAllocationPanel)

目的: CapitalAllocationPanel.tsx(639 行)の KPI 表示部分を Tremor コンポーネントに置き換えて、実装品質とバンドルサイズを実測する。

2-A. Tremor 依存追加

npm install @tremor/react
# v3 の場合は追加で: npm install @headlessui/react (依存)

2-B. 置き換えターゲット

CapitalAllocationPanel 内の以下の表示を Tremor コンポーネントに移行する。

現状の実装Tremor 置換コンポーネント
3 バケツ残高バー(カスタム div + CSS)<ProgressBar /> × 3 (color="blue" / "amber" / "rose")
ROE / Rule of 40 スコア表示(数値 + ラベル)<Metric /> + <Badge />
アロケーション提案カード(手書き card div)<Card /> + <List />
機会損失金額ハイライト(カスタム CSS)<Callout /> (color="rose")

移行方針: 既存コンポーネントを一括置換せず、セクション単位で段階的に置き換える。Tremor コンポーネントには .tremor-root クラスを持つ親 <div> でラップして CSS スコープを確保する。

// 移行前
<div className="cap-alloc-bucket-bar">
  <div style={{ width: `${defenseRatio * 100}%` }} className="bucket-defense" />
  ...
</div>

// 移行後
<div className="tremor-root">
  <ProgressBar value={defenseRatio * 100} color="blue" label="防衛" />
  <ProgressBar value={strategicRatio * 100} color="amber" label="戦略投資" />
  <ProgressBar value={surplusRatio * 100} color="rose" label="純余剰" />
</div>

2-C. ゴーサイン判定(バンドルサイズ計測)

Phase 2 完了後に npm run build:cockpit を実行し、サイズを計測する。

ls -la templates/financial_cockpit.html
計測結果判断
600 KB 以内✅ Phase 3・MAS-358 への展開を継続
600 KB 〜 800 KB⚠️ Tremor の使用範囲を限定(KPI カード / バーのみ)して再計測
800 KB 超⚠️ ファイル分割を前提に Tremor 採用を継続vite-plugin-singlefile による 1 HTML インライン化を複数ファイルに分割(cockpit / multiyear / sidebar を独立エントリに)することでサイズを分散させる。MAS-357 Firebase Hosting 移行と合わせて対処。Tremor 自体の採用は撤回しない。

Phase 3 — v0.dev ワークフロー確立

目的: v0.app を使ったコンポーネント高速生成フローをチームの開発標準として文書化する。

3-A. v0.dev の位置づけと制限

v0.dev は 外部 Web ツール であり npm 依存ではない。出力されたコードを手動でプロジェクトにコピーして使う。

制限事項:

  • v0 の産物は Tailwind + shadcn/ui ベースのため、Phase 1 完了(Tailwind 導入済み)が前提
  • google.script.run / GAS API 等のビジネスロジックは v0 では生成できない → UI シェルのみ生成してロジックを後付けする
  • v0 産物の外部 CDN 参照(フォントリンク等)は GAS CSP 違反 → 産物受け取り後に必ず削除

3-B. cockpit パネル向けプロンプトテンプレート

v0.dev に入力するプロンプトのひな型を確立する。

以下の仕様で React + Tailwind + Tremor のダッシュボードパネルを生成してください。

## コンテキスト
- GAS Web App 内の iframe で動作する SPA
- 外部フォント・外部 CDN の参照禁止(import 文でのみ依存解決)
- 既存: 背景色 #1e2330 のダークナビ + #f5f5f5 のメインコンテンツ

## 生成するコンポーネント
[コンポーネント名と要件をここに記述]

## 使用可能なコンポーネント
- Tremor: Card, Metric, Badge, ProgressBar, List, Callout, AreaChart
- shadcn/ui: Button, Select, Tabs

## 制約
- Props 経由で全データを受け取る(fetch 禁止)
- className は既存の cockpit-section クラスと共存可能な形で

3-C. ワークフロー文書

以下のフローを docs/_internal/ai_agent_tips.md の「§8 フロントエンド UI 生成」として追記する。

1. v0.app でプロンプト入力(上記テンプレート使用)
2. 産物コードをコピー
3. 外部 CDN 参照を除去(import 'https://...' 等を削除)
4. .tremor-root ラッパーを追加
5. Props 型定義を sidebar_api.d.ts のデータ型に合わせる
6. npm run build:cockpit でサイズ確認
7. 動作確認(dev サーバーまたは GAS dev 環境)

エッジケース・異常系

#条件検知方法期待される挙動
1Tailwind preflight が既存 cockpit.css の * リセットと二重適用ビルド後のブラウザ確認で既存パネルの見た目が崩れるtremor-scope.css@scope 境界を調整して既存 CSS を保護
2financial_cockpit.html が 800KB 超ls -la templates/financial_cockpit.html で計測ファイル分割(複数 HTML エントリ化)でサイズ分散。Tremor 採用は継続。MAS-357 Firebase Hosting 移行と合わせて対処
3Tremor v3 の API が破壊的変更を含むnpm install @tremor/react 後に TypeScript エラーv2 (@tremor/[email protected]) に固定してTailwind v3 で代替
4v0 産物に import ... from 'https://...' が含まれるコード貼り付け後の TypeScript エラー / ビルドエラー該当 import を削除または npm パッケージに置き換え
5Tremor コンポーネントの color prop が Tailwind の JIT に認識されない(動的クラス問題)ビルド後にゲージの色が出ないtailwind.config.tssafelist に使用カラーを列挙
6.tremor-root 外で Tremor コンポーネントを使用Tremor のスタイルが適用されない(無色・サイズなし)すべての Tremor 使用箇所に .tremor-root ラッパーを追加するルールを周知
7vite-plugin-singlefile@tailwindcss/vite の処理順序衝突ビルドエラー or CSS がインライン化されないvite.config.tstailwindcss()viteSingleFile() より前に配置
8npm run build:multiyear でも Tremor CSS が二重インライン化multiyear.html のサイズが過大tremor-scope.css を cockpit エントリ HTML のみで import し、multiyear は未使用のままにする
9Tailwind の dark: variants が GAS の配色と干渉ダークモード適用で文字色が白抜けするtailwind.config.tsdarkMode: 'class' を設定し、.dark クラスを付与しない限り適用されないようにする
10Tremor の AreaChart が chart.js と競合同一パネル内で両チャートが表示されないTremor Chart は Recharts ベース。chart.js と共存は可能だが不要ならチャートは chart.js に統一

ファイル変更マトリクス

ファイル変更種別変更内容
webapp_client/package.json変更tailwindcss, @tailwindcss/vite, @tremor/react を追加
webapp_client/vite.config.ts変更tailwindcss() プラグイン追加(viteSingleFile() の前)
webapp_client/src/styles/tremor-scope.css新規Tailwind を .tremor-root スコープに閉じ込める CSS
webapp_client/src/styles/cockpit.css変更@import './tremor-scope.css' を先頭に追加(cockpit / multiyear エントリのみ)
webapp_client/src/cockpit/CapitalAllocationPanel.tsx変更Phase 2: KPI 表示部分を Tremor コンポーネントに置き換え
docs/_internal/ai_agent_tips.md変更Phase 3: §8 フロントエンド UI 生成(v0.dev ワークフロー)追記

推奨実行モデル

ステップ推奨モデル理由
Phase 1 Tailwind 導入Haikuvite.config.ts + package.json の機械的な変更
Phase 1 CSS スコープ設計Sonnet既存 cockpit.css との共存判断が必要
Phase 2 Tremor 試験移行Sonnet既存 JSX パターンを読んで対称的な Tremor コンポーネントに置き換える判断
Phase 3 ワークフロー文書化Haiku決まったフローをテンプレート化するのみ

受け入れ条件

  • npm run build:cockpit が成功する(サイズが 800KB 超の場合はファイル分割で対処・Tremor 採用は継続)
  • npm run type-check がエラーなし
  • 既存コックピットパネル(CompensationStrategyPanel / SoloFinancialStatementsPanel 等)の表示に変化なし
  • CapitalAllocationPanel の 3 バケツバー / ROE / Rule of 40 が Tremor コンポーネントで表示される
  • v0.dev プロンプトテンプレートが ai_agent_tips.md に追記されている
  • .tremor-root 外のパネルには Tailwind の影響が出ない

依存関係

  • MAS-355 部分完了: CapitalAllocationPanel.tsx の試験ターゲットとして使用するため、Phase 2 の CSS/JSX 構造を確認済みであること(実装済み・webapp_client/src/cockpit/CapitalAllocationPanel.tsx として存在確認済み)
  • 後続 MAS-358: 本案件完了後、CockpitNavSidebar.tsx 実装で Tremor Sidebar / SidebarItem を活用できる
  • 後続 MAS-357: Firebase Hosting 移行後も Tailwind / Tremor は動作継続(npm バンドルのため外部 CDN 参照なし)

Phase 4: ローカル UI 開発環境 + ビジュアル回帰テスト(実装済)

背景

GAS HtmlService は Google 認証が必要なため、UI の確認のたびに clasp push → デプロイ(20〜30秒)が必要だった。 Playwright によるスクリーンショット比較も GAS URL では自動化が困難。

解決策

Vite dev server + GAS モック + Playwright の 3 層構成で、GAS なしで UI 開発・テストができる環境を整備した。

UI の形を作る  → localhost で高速イテレーション(GAS 不要)
     ↓
形が決まったら → GAS にデプロイして実データで最終確認

構成ファイル

ファイル役割
webapp_client/src/dev-gas-mock.tsgoogle.script.run を Proxy で差し替え、fixture データで cockpit を起動可能にする。本番ビルドでは import.meta.env.DEV === false により tree-shaken される
webapp_client/src/cockpit-main.tsxDEV 時のみ dev-gas-mock.ts を動的 import(if (import.meta.env.DEV) で条件分岐)
webapp_client/playwright.config.tsPlaywright 設定。Vite dev server を自動起動し、スクリーンショット差分 2% 超で失敗
webapp_client/tests/cockpit-visual.spec.tsビジュアル回帰テスト 4 件(全体・ヘッダー・4アプローチ比較・資本効率パネル)
webapp_client/tests/*/snapshots/*.pngbaseline スクリーンショット(macOS: -darwin.png / Linux CI: -linux.png
.github/workflows/ui-test.ymlPR 時に自動実行。Linux baseline が未存在の場合は自動生成してコミット

開発フロー

# 1. ローカルで UI を確認
npm run dev:cockpit          # http://localhost:5173/financial_cockpit.html

# 2. 変更後にテストで差分確認
npm run test:ui              # 差分 2% 超で失敗 + diff 画像生成

# 3. 意図した変更なら baseline を更新
npm run test:ui:update       # 新しいスクリーンショットを baseline として保存

# 4. GAS にデプロイして実データで最終確認
npm run deploy:dev

CI フロー(GitHub Actions)

PR 作成
  ↓
ui-test.yml が自動実行(Ubuntu)
  ↓
Linux snapshot が未存在 → 自動生成してコミット(初回のみ)
Linux snapshot が存在  → 前回 baseline と比較
  ↓
差分なし → 通過
差分あり → 失敗 + visual-diff artifact に diff 画像をアップロード

PR の Artifacts から visual-diff をダウンロードすると、変更箇所を赤くハイライトした diff 画像で確認できる。 意図した UI 変更の場合は npm run test:ui:update を実行して baseline を更新してから再 push する。

制約・注意事項

  • GAS 実データとの差異: localhost はモックデータ(固定値)を使用。実データの edge case(金額が極端に大きい等)はGAS で最終確認が必要
  • GAS iframe 環境: HtmlService の iframe 内での挙動は Playwright では再現できない
  • プラットフォーム別 baseline: macOS(-darwin.png)と Linux CI(-linux.png)でフォント描画が微妙に異なるため別ファイルで管理
  • ブラウザ: Chromium(Chrome)のみ。Safari / Firefox は対象外

変更履歴

日時バージョン変更内容
2026-05-03v1.2Phase 4(ローカル UI 開発環境 + ビジュアル回帰テスト)追記。実装ステータスを「完了」に更新
2026-05-03v1.1go/no-go ゲート更新: 800KB 超の場合「Tremor 見直し」→「ファイル分割で継続」に方針変更。ファイル分割は MAS-357 Firebase Hosting 移行と合わせて対処する方針が確定したため
2026-05-03v1.0初版起票