dashboard-builder について
dashboard-builderは、データ可視化ダッシュボードを開発するチーム向けのスキルです。Next.jsやRechartsなどを使うUI指針とコンポーネント例を提供し、プロジェクトの初期化・起動スクリプトも含みます。
主な機能
- 01ダークテーマのカード型ダッシュボード設計
- 02KPI、データカード、数値アニメーションの例
- 03スクロール、ホバー、進捗アニメーションの指針
- 04API、データベース、静的データに対応
- 05プロジェクトの初期化と開発サーバー起動スクリプト
活用シーン
- 01大画面向けの運用ダッシュボード構築
- 02KPIや時系列データの可視化
- 03APIやデータベースのデータ連携
- 04Next.jsダッシュボードの迅速な初期化
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ qwenlm/ qwen-code-examples --skill dashboard-builderインストール後、Claude Code で/dashboard-builderを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
ADDITIONAL_COMPONENTS.mdDATA_INTEGRATION.mdpackage.jsonQUICKSTART.mdsetup-and-run.shreferences/big-screen-ui.mddata-api-integration.mdrecharts-patterns.md詳細
ダッシュボードビルダー
モダンな UI、豊富なビジュアライゼーション、動的なエフェクトを備えた、プロフェッショナルなデータ可視化ダッシュボードを構築します。
技術スタック
- フロントエンド: Next.js 14+(App Router)+ React 18+
- UI コンポーネント: shadcn/ui + Tailwind CSS
- チャート: Recharts
- アニメーション: Framer Motion
- バックエンド: Express.js
- データソース: 設定可能な API、データベース、または静的データ
デザイン要件
ビジュアルスタイル(プロフェッショナルな PC ダッシュボード)
- ダークテーマと控えめなグラデーション(派手にしない)
- すっきりとしたカードデザインに柔らかなシャドウとボーダー
- プロフェッショナルなカラーパレット:
- 背景: slate-900 (#0f172a) から slate-950 (#020617)
- カード: slate-800/50 に控えめなボーダー
- 成功: emerald-500 (#10b981)
- 危険: rose-500 (#f43f5e)
- アクセント: blue-500 (#3b82f6)
- 大画面(4K、ウルトラワイド)に最適化した大きな文字
- グリッドベースのレイアウトで明確な視覚的階層
アニメーション要件
- 表示時のアニメーション: ページ読み込み時に控えめなフェードインと上方向へのスライド
- スクロール連動アニメーション: カードが画面内に入るとアニメーション
- 数値カウントアニメーション: 値が滑らかに変化
- プログレスバーのアニメーション: イージング付きでバーが伸長
- ホバーエフェクト: カードがわずかに浮き上がり、シャドウが付く
- ライブデータのパルス: データ更新時に控えめなインジケーターを表示
プロジェクト構成
project-root/
├── app/
│ ├── layout.tsx
│ ├── page.tsx # Landing/input page
│ └── dashboard/
│ └── page.tsx # Results dashboard
├── components/
│ ├── ui/ # shadcn components
│ ├── charts/
│ │ ├── LineChart.tsx
│ │ ├── BarChart.tsx
│ │ ├── AreaChart.tsx
│ │ └── PieChart.tsx
│ ├── dashboard/
│ │ ├── KPICard.tsx
│ │ ├── DataCard.tsx
│ │ ├── StatsPanel.tsx
│ │ └── DataTable.tsx
│ └── effects/
│ └── AnimatedNumber.tsx
├── lib/
│ ├── api.ts
│ ├── utils.ts
│ └── animations.ts
├── server/
│ ├── index.ts
│ ├── routes/
│ │ └── data.ts
│ └── services/
│ └── dataService.ts
└── styles/
└── globals.css
PC 大画面レイアウト
4K ダッシュボードグリッド(3840x2160)
export default function DashboardPage() {
return (
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-slate-900 to-slate-950 p-6">
{/* Header */}
<header className="flex items-center justify-between mb-8">
<h1 className="text-4xl font-bold text-white">Dashboard</h1>
<div className="flex items-center gap-4">
<LiveIndicator />
<span className="text-slate-400 text-lg">Last updated: {time}</span>
</div>
</header>
{/* KPI Row */}
<div className="grid grid-cols-4 gap-6 mb-8">
<KPICard title="Metric 1" value={125000} prefix="$" trend={5.2} />
<KPICard title="Metric 2" value={2340} prefix="$" trend={1.8} />
<KPICard title="Metric 3" value={18.5} suffix="%" trend={0} />
<KPICard title="Metric 4" value={12} trend={0} />
</div>
{/* Main Content */}
<div className="grid grid-cols-12 gap-6">
{/* Main Chart - 8 columns */}
<div className="col-span-8">
<Card className="h-[500px]">
<CardHeader>
<CardTitle>Main Metric Trend</CardTitle>
</CardHeader>
<CardContent>
<LineChart data={metricData} />
</CardContent>
</Card>
</div>
{/* Side Panel - 4 columns */}
<div className="col-span-4 space-y-6">
<Card>
<CardHeader><CardTitle>Top Items</CardTitle></CardHeader>
<CardContent>
<DataTable data={topItems} />
</CardContent>
</Card>
<Card>
<CardHeader><CardTitle>Comparison</CardTitle></CardHeader>
<CardContent>
<PieChart data={comparisonData} />
</CardContent>
</Card>
</div>
{/* Timeline Row - Full width */}
<div className="col-span-12">
<h2 className="text-2xl font-semibold text-white mb-4">Trend Analysis</h2>
<div className="grid grid-cols-4 gap-6">
{timelineData.map(item => (
<DataCard key={item.period} {...item} />
))}
</div>
</div>
</div>
</div>
)
}
コアコンポーネント
KPI カード
import { Card, CardContent } from "@/components/ui/card"
import { TrendingUp, TrendingDown, Minus } from "lucide-react"
import { AnimatedNumber } from "./AnimatedNumber"
interface KPICardProps {
title: string
value: number
prefix?: string
suffix?: string
trend?: number
}
export function KPICard({ title, value, prefix = '', suffix = '', trend = 0 }: KPICardProps) {
return (
<Card className="bg-slate-800/50 border-slate-700/50">
<CardContent className="pt-6">
<p className="text-slate-400 text-sm font-medium mb-2">{title}</p>
<div className="flex items-end justify-between">
<span className="text-3xl font-bold text-white">
{prefix}<AnimatedNumber value={value} />{suffix}
</span>
{trend !== 0 && (
<span className={cn(
"flex items-center text-sm font-medium",
trend > 0 ? "text-emerald-500" : "text-rose-500"
)}>
{trend > 0 ? <TrendingUp className="w-4 h-4 mr-1" /> : <TrendingDown className="w-4 h-4 mr-1" />}
{Math.abs(trend)}%
</span>
)}
</div>
</CardContent>
</Card>
)
}
データカード(汎用タイムラインカード)
'use client'
import { motion } from 'framer-motion'
import { useInView } from 'framer-motion'
import { useRef } from 'react'
import { Card, CardContent } from "@/components/ui/card"
interface DataCardProps {
period: string
currentValue: number
baselineValue: number
percentChange: number
description: string
}
export function DataCard({ period, currentValue, baselineValue, percentChange, description }: DataCardProps) {
const ref = useRef(null)
const isInView = useInView(ref, { once: true, margin: "-50px" })
const isPositive = percentChange >= 0
return (
<motion.div
ref={ref}
initial={{ opacity: 0, y: 30 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.5, ease: "easeOut" }}
>
<Card className="bg-slate-800/50 border-slate-700/50 overflow-hidden">
<div className={cn(
"h-1",
isPositive ? "bg-emerald-500" : "bg-rose-500"
)} />
<CardContent className="pt-6">
<div className="flex items-baseline gap-2 mb-4">
<span className="text-4xl font-bold text-white">{period}</span>
</div>
<div className="space-y-3">
<div className="flex justify-between text-sm">
<span className="text-slate-400">Baseline Value</span>
<span className="text-slate-300">{baselineValue}</span>
</div>
<div className="flex justify-between text-sm">
<span className="text-slate-400">Current Value</span>
<span className="text-white font-medium">{currentValue}</span>
</div>
{/* Change percentage */}
<div className={cn(
"text-2xl font-bold text-center py-3 rounded-lg",
isPositive ? "text-emerald-500 bg-emerald-500/10" : "text-rose-500 bg-rose-500/10"
)}>
{isPositive ? '+' : ''}{percentChange.toFixed(2)}%
</div>
{/* Progress bar */}
<div className="h-1.5 bg-slate-700 rounded-full overflow-hidden">
<motion.div
initial={{ width: 0 }}
animate={isInView ? { width: `${Math.min(Math.abs(percentChange), 100)}%` } : {}}
transition={{ duration: 1, ease: "easeOut", delay: 0.3 }}
className={cn(
"h-full rounded-full",
isPositive ? "bg-emerald-500" : "bg-rose-500"
)}
/>
</div>
<p className="text-slate-400 text-sm">{description}</p>
</div>
</CardContent>
</Card>
</motion.div>
)
}
アニメーション付き数値
'use client'
import { useEffect, useState } from 'react'
import { animate } from 'framer-motion'
interface AnimatedNumberProps {
value: number
decimals?: number
duration?: number
}
export function AnimatedNumber({ value, decimals = 2, duration = 1 }: AnimatedNumberProps) {
const [display, setDisplay] = useState(0)
useEffect(() => {
const controls = animate(display, value, {
duration,
ease: "easeOut",
onUpdate: (v) => setDisplay(v)
})
return () => controls.stop()
}, [value, duration])
return <>{display.toFixed(decimals)}</>
}
ライブインジケーター
export function LiveIndicator() {
return (
<div className="flex items-center gap-2">
<span className="relative flex h-3 w-3">
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75" />
<span className="relative inline-flex rounded-full h-3 w-3 bg-emerald-500" />
</span>
<span className="text-emerald-500 text-sm font-medium">LIVE</span>
</div>
)
}
自動化スクリプト
自動セットアップと起動
開発環境のセットアップを簡単にするため、次の処理を行う自動化スクリプトを用意しています。
- 必要な依存関係をすべて含む新しい Next.js プロジェクトを作成する
- shadcn/ui コンポーネントをインストールする
- サンプルのダッシュボードコンポーネントをセットアップする
- 開発サーバーを起動する
次のコマンドでセットアップスクリプトを実行します。
./setup-and-run.sh
スクリプトではプロジェクト名の入力が求められ、すべてのセットアップ手順が自動で実行された後、開発サーバーが http://localhost:3000 で起動します。
手動セットアップ
手動でセットアップする場合は、次の手順に従ってください。
-
新しい Next.js アプリを作成します。
npx create-next-app@latest my-dashboard --typescript --tailwind --eslint -
必要な依存関係をインストールします。
cd my-dashboard npm install shadcn-ui recharts framer-motion lucide-react -
shadcn/ui を初期化し、コンポーネントを追加します。
npx shadcn-ui@latest init npx shadcn-ui@latest add card button badge table scroll-area alert -
開発サーバーを起動します。
npm run dev
CSS スタイル
/* globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--primary: 217.2 91.2% 59.8%;
--primary-foreground: 222.2 47.4% 11.2%;
}
/* Big screen typography */
@media (min-width: 2560px) {
html { font-size: 18px; }
}
@media (min-width: 3840px) {
html { font-size: 20px; }
}
/* Smooth scrolling */
html {
scroll-behavior: smooth;
}
/* Card hover effect */
.card-hover {
@apply transition-all duration-300;
}
.card-hover:hover {
@apply -translate-y-1 shadow-lg shadow-slate-900/50;
}
/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
自動更新フック
'use client'
import { useEffect, useState, useCallback } from 'react'
export function useLiveData<T>(
fetcher: () => Promise<T>,
interval = 60000
) {
const [data, setData] = useState<T | null>(null)
const [loading, setLoading] = useState(true)
const [isRefreshing, setIsRefreshing] = useState(false)
const [lastUpdated, setLastUpdated] = useState<Date | null>(null)
const refresh = useCallback(async () => {
setIsRefreshing(true)
try {
const result = await fetcher()
setData(result)
setLastUpdated(new Date())
} finally {
setLoading(false)
setTimeout(() => setIsRefreshing(false), 300)
}
}, [fetcher])
useEffect(() => {
refresh()
const timer = setInterval(refresh, interval)
return () => clearInterval(timer)
}, [refresh, interval])
return { data, loading, isRefreshing, lastUpdated, refresh }
}
リソース
- references/data-api-integration.md - API エンドポイント、データ処理、エラー処理
- references/recharts-patterns.md - チャートのパターンとカスタマイズ
- references/big-screen-ui.md - 大画面レイアウトのガイドライン
- QUICKSTART.md - クイックスタート用テンプレートと初期化ガイド
- DATA_INTEGRATION.md - データ統合の詳細な手順と例
- ADDITIONAL_COMPONENTS.md - 大画面ダッシュボード向けの追加コンポーネント
コメント
Markdown 対応