RoomCraft — v1.0.0 — 2026-03-19
作成日: 2026-03-19 ベース: shadcn/ui + Radix UI + Three.js (React Three Fiber) スタイリング: Tailwind CSS アニメーション: motion/react アイコン: Lucide React
transform / opacity プロパティのみ(200ms以下)src/
├── components/
│ ├── ui/ # shadcn/ui ベースコンポーネント
│ │ ├── button.tsx
│ │ ├── card.tsx
│ │ ├── input.tsx
│ │ ├── slider.tsx
│ │ └── ...
│ ├── furniture/ # 家具関連コンポーネント
│ │ ├── FurnitureCard.tsx
│ │ ├── FurnitureCarousel.tsx
│ │ ├── FurnitureFilter.tsx
│ │ └── FurnitureDetail.tsx
│ ├── viewer/ # 3D/2Dビューア
│ │ ├── RoomViewport.tsx
│ │ ├── FloorPlanEditor.tsx
│ │ ├── FurnitureModel.tsx
│ │ └── ViewportControls.tsx
│ ├── layout/ # レイアウト
│ │ ├── Header.tsx
│ │ ├── Sidebar.tsx
│ │ ├── BottomSheet.tsx
│ │ └── MakerLayout.tsx
│ └── common/ # 共通コンポーネント
│ ├── ColorPicker.tsx
│ ├── LoadingState.tsx
│ └── EmptyState.tsx
// カラートークン上書き(tailwind.config.ts)
// primary: #2C3E2D, accent: #C8956C
// バリアント
<Button variant="default"> // primary #2C3E2D, white text
<Button variant="secondary"> // secondary #F5F0E8, charcoal text
<Button variant="outline"> // border #D4D0C8, transparent bg
<Button variant="ghost"> // no border, hover: #F5F0E8
<Button variant="cta"> // accent #C8956C, white text - 主要CTA
<Button variant="sage"> // sage green #5B8A72 - サブアクション
スタイル定義:
rounded-md (8px)shadow-[0_1px_2px_rgba(44,62,45,0.06)]font-montserrat font-medium text-sm (Montserrat 500, 0.875rem)min-h-[44px] min-w-[44px]transition-opacity duration-150 (opacity のみ)// 家具商品カード
<FurnitureCard
product={product}
onSelect={handleSelect}
variant="grid" // グリッド表示(正方形サムネイル)
variant="list" // リスト表示(横長)
/>
スタイル定義:
rounded-xl (12px)shadow-[0_4px_12px_rgba(44,62,45,0.06)]hover:shadow-[0_8px_24px_rgba(44,62,45,0.12)] hover:-translate-y-0.5transition-[shadow,transform] duration-200aspect-square (正方形統一)bg-[#FAFAF7] (オフホワイト)<Input placeholder="家具を検索..." />
<Input type="search" icon={<Search />} />
スタイル定義:
rounded-md (8px)border border-[#D4D0C8]focus:border-[#2C3E2D] focus:ring-1 focus:ring-[#2C3E2D]font-noto-sans-jp text-base<PriceRangeSlider
min={0}
max={1000000}
step={1000}
defaultValue={[0, 500000]}
formatValue={(v) => `¥${v.toLocaleString()}`}
/>
スタイル定義:
bg-[#D4D0C8]bg-[#2C3E2D]bg-white border-2 border-[#2C3E2D] rounded-full w-5 h-5min-h-[44px]import { Canvas } from '@react-three/fiber'
import { OrbitControls, Environment, useGLTF } from '@react-three/drei'
import { motion } from 'motion/react'
export function RoomViewport({ roomConfig, selectedFurniture }) {
return (
<div className="relative w-full h-full rounded-none bg-[#FAFAF7]">
{/* Three.js Canvas */}
<Canvas
camera={{ position: [0, 5, 10], fov: 60 }}
gl={{ antialias: true, powerPreference: 'high-performance' }}
>
<Suspense fallback={<ModelLoadingFallback />}>
<RoomScene roomConfig={roomConfig} />
<FurnitureInstances furniture={selectedFurniture} />
<Environment preset="apartment" />
<OrbitControls
enableDamping
dampingFactor={0.05}
minPolarAngle={0}
maxPolarAngle={Math.PI / 2}
/>
</Suspense>
</Canvas>
{/* UIオーバーレイ(半透明) */}
<ViewportControls className="absolute bottom-4 right-4" />
<FurnitureSelectionHUD className="absolute top-4 left-4" />
</div>
)
}
3D設計要件:
#FAFAF7 ベースbg-[rgba(26,29,26,0.7)] backdrop-blur-sm (半透明)THREE.LineSegments または outlinePass)tabIndex={0})<Suspense> + プログレスバー)export function ModelLoadingFallback() {
return (
<motion.div
className="absolute inset-0 flex flex-col items-center justify-center bg-[#FAFAF7]"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.15 }}
>
{/* シルエットプレースホルダー */}
<div className="w-48 h-32 rounded-lg bg-[#D4D0C8] animate-pulse" />
{/* プログレスバー */}
<div className="mt-4 w-48 h-1 bg-[#D4D0C8] rounded-full overflow-hidden">
<motion.div
className="h-full bg-[#2C3E2D] rounded-full"
initial={{ width: "0%" }}
animate={{ width: "100%" }}
transition={{ duration: 2, ease: "easeOut" }}
/>
</div>
<p className="mt-2 text-sm text-[#8A8A80] font-noto-sans-jp">
家具モデルを読み込み中...
</p>
</motion.div>
)
}
import Konva from 'konva'
import { Stage, Layer, Rect, Line, Transformer } from 'react-konva'
export function FloorPlanEditor({ roomConfig, onRoomUpdate }) {
// 2D間取り編集
// - 壁・部屋形状の描画
// - 家具の2D投影配置
// - グリッドスナップ
// - 寸法ラベル表示
}
import { motion, AnimatePresence } from 'motion/react'
export function FurnitureCarousel({ items }) {
return (
<motion.div
className="flex gap-4 overflow-x-auto scrollbar-hide px-4"
drag="x"
dragConstraints={{ right: 0, left: -totalWidth }}
whileTap={{ cursor: "grabbing" }}
>
{items.map((item) => (
<motion.div
key={item.id}
className="flex-none"
whileHover={{ scale: 1.02 }} // transform のみ
transition={{ duration: 0.15 }}
>
<FurnitureCard product={item} />
</motion.div>
))}
</motion.div>
)
}
import { motion, useDragControls } from 'motion/react'
export function BottomSheet({ isOpen, onClose, children }) {
return (
<motion.div
className="fixed bottom-0 left-0 right-0 bg-white rounded-t-2xl shadow-modal z-50"
style={{ paddingBottom: 'env(safe-area-inset-bottom)' }}
initial={{ y: "100%" }}
animate={{ y: isOpen ? 0 : "100%" }}
transition={{ type: "spring", damping: 30, stiffness: 300 }}
drag="y"
dragConstraints={{ top: 0 }}
onDragEnd={handleDragEnd}
>
{/* ドラッグハンドル */}
<div className="flex justify-center pt-3 pb-2">
<div className="w-10 h-1 bg-[#D4D0C8] rounded-full" />
</div>
{children}
</motion.div>
)
}
export function EmptyState({ type }: { type: 'room' | 'search' | 'error' }) {
const configs = {
room: {
illustration: <RoomIllustration />,
title: "家具を配置しましょう",
description: "右のパネルから家具を選んで、ドラッグ&ドロップで配置できます",
action: <Button variant="cta">家具を選ぶ</Button>,
},
search: {
illustration: <SearchIllustration />,
title: "検索結果がありません",
description: "条件を変更してもう一度お試しください",
action: <Button variant="outline" onClick={clearFilters}>条件をリセット</Button>,
},
error: {
illustration: <ErrorIllustration />,
title: "エラーが発生しました",
description: "しばらく時間をおいてから再度お試しください",
action: <Button variant="outline" onClick={retry}>再試行</Button>,
},
}
// ...
}
// 行ごとの微妙な背景色差で可読性向上
<DataTable
data={products}
columns={columns}
rowClassName={(row, index) =>
index % 2 === 0 ? "bg-white" : "bg-[#FAFAF7]"
}
/>
// Earth tone系カラーで統一
const chartColors = {
primary: "#2C3E2D",
secondary: "#5B8A72",
accent: "#C8956C",
muted: "#D4D0C8",
}
// 正方形統一
<ProductThumbnail
src={product.image}
alt={product.name}
className="aspect-square w-16 h-16 rounded-md object-cover"
/>
| コンポーネント | 要件 |
|---|---|
| 全インタラクティブ要素 | aria-label または可視ラベル必須 |
| 3Dビューポート | role="application" + aria-label="3D家具配置ビューア" |
| アイコンボタン | aria-label 必須(<Button aria-label="家具を追加"> ) |
| フォーム | <label> または aria-labelledby 必須 |
| ローディング | aria-busy="true" + aria-live="polite" |
| エラーメッセージ | role="alert" |
| フォーカスインジケーター | focus-visible:ring-2 focus-visible:ring-[#2C3E2D] 常時表示 |
| キーボード操作 | 全操作対応(3Dビュー含む。矢印キーで軌道回転) |
| カテゴリ | 禁止 | 理由 |
|---|---|---|
| フォント | Inter, Roboto, Arial, Space Grotesk | AI生成典型 |
| カラー | 紫グラデーション on 白背景 | AI生成典型 |
| アニメーション | layout プロパティ (width, height, top, left) のアニメーション | CLS悪化 |
| アニメーション | 200ms超のインタラクションアニメーション | 体感遅延 |
| エフェクト | 過剰なドロップシャドウ | AI生成典型 |
| エフェクト | グロー・ネオン効果 | デザイン方針外 |
| 3D | 技術デモ的な暗いビューポート | 没入感の逆効果 |
Generated for RoomCraft - Phase 2: Design CCAGI SDK v3.14.4