Skip to main content

data-visualization

Build interactive, accessible charts, graphs, and data dashboards using Recharts, D3, or Victory. Use when the user says "chart", "graph", "data visualization", "analytics dashboard", "metrics display", "D3", "Recharts", "time-series", or "data display".

소스 정보

저장소
kensaurus/cursor-kenji
최근 소스 활동
2026년 9월 12일 14:20
감지된 SKILL.md 언어
영어
스타
9
포크
0

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
data-visualization
description
Build interactive, accessible charts, graphs, and data dashboards using Recharts, D3, or Victory. Use when the user says "chart", "graph", "data visualization", "analytics dashboard", "metrics display", "D3", "Recharts", "time-series", or "data display".
license
MIT
# Data Visualization Skill **Degree of freedom: MIXED.** Chart type and encoding `[HIGH freedom]`; existing-library/token probes and a11y fallback `[LOW freedom — run exactly]`. ## How to reason 1. **Observe** — existing chart library, tokens, data shape 2. **Interpret** — reuse a component vs new chart type 3. **Classify** — line / bar / area / donut / sparkline / table-fallback 4. **Severity** — inaccessible or empty-state-less chart outranks a missing gradient ## Worked example > **Observe:** `package.json` has recharts; no `src/components/charts`; tokens define `--primary`. > **Interpret:** reuse Recharts + tokens, not D3. > **Classify:** `ResponsiveContainer` line chart + sr-only table; skeleton while loading. > **Verify:** resizes; screen-reader table present; no second chart library. ## Self-critique before reporting - **Existing lib** — grepped `package.json`; did not add a second chart library - **Accessible** — sr-only table or text alternative present - **States** — loading + empty handled - **Right owner** — event taxonomy → `audit-analytics`; empty/loading frames → `audit-ui-states` Create beautiful, accessible, and interactive data visualizations for dashboards and reports. Product-event coverage ("are we tracking the right events?") is `audit-analytics`, not this skill. Empty/loading chart frames are `audit-ui-states`. ## Check existing first [LOW freedom — run exactly] **Before creating ANY visualization, verify:** 1. **Check for existing chart libraries:** ```bash cat package.json | grep -i "recharts\|chart\|d3\|visx\|nivo\|tremor" rg "LineChart|BarChart|PieChart" --type tsx -l | head -10 ``` 2. **Check for existing chart components:** ```bash ls -la src/components/charts/ src/components/dashboard/ 2>/dev/null rg "ResponsiveContainer|Chart" --type tsx | head -10 ``` 3. **Check for design tokens:** ```bash cat tailwind.config.* | grep -A10 "chart\|colors" ``` **Why:** Use existing chart library and styling conventions. ## Recharts (Recommended for React) [HIGH freedom] ### Line Chart ```tsx 'use client' import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend, } from 'recharts' const data = [ { month: 'Jan', revenue: 4000, users: 2400 }, { month: 'Feb', revenue: 3000, users: 1398 }, { month: 'Mar', revenue: 2000, users: 9800 }, ] export function RevenueChart() { return ( <ResponsiveContainer width="100%" height={350}> <LineChart data={data}> <CartesianGrid strokeDasharray="3 3" className="stroke-muted" /> <XAxis dataKey="month" className="text-xs fill-muted-foreground" tickLine={false} axisLine={false} /> <YAxis className="text-xs fill-muted-foreground" tickLine={false} axisLine={false} tickFormatter={(value) => `$${value}`} /> <Tooltip content={<CustomTooltip />} cursor={{ stroke: 'hsl(var(--muted))' }} /> <Legend /> <Line type="monotone" dataKey="revenue" stroke="hsl(var(--primary))" strokeWidth={2} dot={{ fill: 'hsl(var(--primary))' }} activeDot={{ r: 6 }} /> <Line type="monotone" dataKey="users" stroke="hsl(var(--secondary))" strokeWidth={2} dot={{ fill: 'hsl(var(--secondary))' }} /> </LineChart> </ResponsiveContainer> ) } function CustomTooltip({ active, payload, label }: any) { if (!active || !payload) return null return ( <div className="rounded-lg border bg-background p-2 shadow-sm"> <p className="text-sm font-medium">{label}</p> {payload.map((entry: any, index: number) => ( <p key={index} className="text-sm" style={{ color: entry.color }}> {entry.name}: {entry.value} </p> ))} </div> ) } ``` ### Bar Chart ```tsx 'use client' import { BarChart, Bar, XAxis, YAxis, ResponsiveContainer, Cell } from 'recharts' const data = [ { name: 'Mon', value: 12 }, { name: 'Tue', value: 19 }, { name: 'Wed', value: 3 }, { name: 'Thu', value: 5 }, { name: 'Fri', value: 2 }, ] export function WeeklyChart() { return ( <ResponsiveContainer width="100%" height={200}> <BarChart data={data}> <XAxis dataKey="name" tickLine={false} axisLine={false} className="text-xs fill-muted-foreground" /> <YAxis hide /> <Bar dataKey="value" radius={[4, 4, 0, 0]} className="fill-primary" > {data.map((entry, index) => ( <Cell key={index} className={index === data.length - 1 ? 'fill-primary' : 'fill-primary/60'} /> ))} </Bar> </BarChart> </ResponsiveContainer> ) } ``` ### Area Chart with Gradient ```tsx 'use client' import { AreaChart, Area, XAxis, YAxis, ResponsiveContainer } from 'recharts' export function GradientAreaChart({ data }: { data: any[] }) { return ( <ResponsiveContainer width="100%" height={200}> <AreaChart data={data}> <defs> <linearGradient id="colorValue" x1="0" y1="0" x2="0" y2="1"> <stop offset="5%" stopColor="hsl(var(--primary))" stopOpacity={0.3} /> <stop offset="95%" stopColor="hsl(var(--primary))" stopOpacity={0} /> </linearGradient> </defs> <XAxis dataKey="date" hide /> <YAxis hide /> <Area type="monotone" dataKey="value" stroke="hsl(var(--primary))" strokeWidth={2} fill="url(#colorValue)" /> </AreaChart> </ResponsiveContainer> ) } ``` ### Pie/Donut Chart ```tsx 'use client' import { PieChart, Pie, Cell, ResponsiveContainer, Label } from 'recharts' const COLORS = [ 'hsl(var(--primary))', 'hsl(var(--secondary))', 'hsl(var(--accent))', 'hsl(var(--muted))', ] export function DonutChart({ data, total }: { data: any[]; total: number }) { return ( <ResponsiveContainer width="100%" height={200}> <PieChart> <Pie data={data} cx="50%" cy="50%" innerRadius={60} outerRadius={80} paddingAngle={2} dataKey="value" > {data.map((_, index) => ( <Cell key={index} fill={COLORS[index % COLORS.length]} /> ))} <Label value={total} position="center" className="fill-foreground text-2xl font-bold" /> </Pie> </PieChart> </ResponsiveContainer> ) } ``` ## Sparklines (Mini Charts) [HIGH freedom] ```tsx 'use client' import { LineChart, Line, ResponsiveContainer } from 'recharts' interface SparklineProps { data: number[] color?: string height?: number } export function Sparkline({ data, color = 'hsl(var(--primary))', height = 40 }: SparklineProps) { const chartData = data.map((value, index) => ({ index, value })) return ( <ResponsiveContainer width="100%" height={height}> <LineChart data={chartData}> <Line type="monotone" dataKey="value" stroke={color} strokeWidth={1.5} dot={false} /> </LineChart> </ResponsiveContainer> ) } // Usage in stats card <div className="flex items-center justify-between"> <div> <p className="text-sm text-muted-foreground">Revenue</p> <p className="text-2xl font-bold">$45,231</p> </div> <div className="w-20"> <Sparkline data={[10, 15, 8, 22, 18, 25, 30]} /> </div> </div> ``` ## Stat Cards with Trends [HIGH freedom] ```tsx import { ArrowUpIcon, ArrowDownIcon } from 'lucide-react' import { cn } from '@/lib/utils' interface StatCardProps { title: string value: string change: number trend: 'up' | 'down' sparklineData?: number[] } export function StatCard({ title, value, change, trend, sparklineData }: StatCardProps) { return ( <div className="rounded-xl border bg-card p-6"> <p className="text-sm font-medium text-muted-foreground">{title}</p> <div className="mt-2 flex items-baseline justify-between"> <p className="text-3xl font-semibold">{value}</p> <div className={cn( 'flex items-center text-sm font-medium', trend === 'up' ? 'text-green-600' : 'text-red-600' )}> {trend === 'up' ? ( <ArrowUpIcon className="mr-1 h-4 w-4" /> ) : ( <ArrowDownIcon className="mr-1 h-4 w-4" /> )} {Math.abs(change)}% </div> </div> {sparklineData && ( <div className="mt-4"> <Sparkline data={sparklineData} color={trend === 'up' ? 'rgb(22 163 74)' : 'rgb(220 38 38)'} /> </div> )} </div> ) } ``` ## Real-time Data Updates [HIGH freedom] ```tsx 'use client' import { useEffect, useState } from 'react' import { LineChart, Line, ResponsiveContainer, YAxis } from 'recharts' export function RealtimeChart() { const [data, setData] = useState<{ time: number; value: number }[]>([]) useEffect(() => { const interval = setInterval(() => { setData((prev) => { const newPoint = { time: Date.now(), value: Math.random() * 100, } // Keep last 20 points const updated = [...prev, newPoint].slice(-20) return updated }) }, 1000) return () => clearInterval(interval) }, []) return ( <ResponsiveContainer width="100%" height={100}> <LineChart data={data}> <YAxis domain={[0, 100]} hide /> <Line type="monotone" dataKey="value" stroke="hsl(var(--primary))" strokeWidth={2} dot={false} isAnimationActive={false} /> </LineChart> </ResponsiveContainer> ) } ``` ## D3.js for Custom Visualizations [HIGH freedom] ```tsx 'use client'
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기