Chart builder
Persona-based AI skill ecosystem — 7 personas, 14 orchestration skills for outbound campaigns, publishing, security, macOS automation, and full-stack dev. Works with Claude Code, Claude Chat, and Agent SDK. AGPL v3 + commercial license.
npx -y skills add 0xjitsu/jitsu-skills --skill chart-builderAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Generates dark-themed, accessible data visualization components using Chart.js or Recharts. Produces glass morphism styled charts that match the user's design system with proper accessibility, responsive sizing, and performance optimization. Triggered when the user asks to create a chart, visualize data, add a graph, or build a dashboard component.
The file declares its own license as AGPL-3.0-or-later. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
6.1 KB, as published. Nobody here has run it
Chart Builder Skill
Dark-themed, accessible data visualization generator.
When to Trigger
Activate this skill when the user:
- Asks to create a chart, graph, or data visualization
- Wants to visualize data from an API, database, or static dataset
- Requests a dashboard component with charts
- Needs to display metrics, trends, or comparisons visually
Stack Selection
| Scenario | Library | Reason |
|---|---|---|
| Standalone chart, no React | Chart.js | Lightweight, canvas-based |
| React/Next.js project | Recharts | Native React components, composable |
| Complex interactive dashboards | Recharts | Better state management integration |
| Static image export needed | Chart.js | Canvas toDataURL() support |
Design System
All charts must follow these design tokens to match the user's dark theme:
Colors
const chartTheme = {
background: '#18181B', // zinc-900
surface: 'rgba(255,255,255,0.05)', // glass bg
border: 'rgba(255,255,255,0.10)', // glass border
gridLines: 'rgba(255,255,255,0.06)',
labelText: '#D4D4D8', // zinc-300
valueText: '#F4F4F5', // zinc-100
tooltip: {
bg: 'rgba(24,24,27,0.90)',
border: 'rgba(255,255,255,0.10)',
blur: '16px',
},
};
Accent Palette
Use these for data series — ordered by visual priority:
| Index | Name | Hex | Use case |
|---|---|---|---|
| 0 | Blue | #3B82F6 | Primary metric |
| 1 | Purple | #8B5CF6 | Secondary metric |
| 2 | Emerald | #10B981 | Positive/growth |
| 3 | Amber | #F59E0B | Warning/attention |
| 4 | Rose | #F43F5E | Negative/decline |
| 5 | Cyan | #06B6D4 | Tertiary metric |
Glass Morphism Container
Wrap every chart in a glass container:
<div className="backdrop-blur-xl bg-white/5 border border-white/10 rounded-2xl p-6">
<h3 className="text-zinc-100 text-lg font-semibold mb-4">{title}</h3>
<div className="min-h-[300px]" role="img" aria-label={ariaLabel}>
{/* Chart component */}
</div>
</div>
Chart Types
Line Chart
Best for: trends over time, continuous data.
- Use gradient fill below the line (
linearGradientfrom accent to transparent) - Smooth curves:
type="monotone"(Recharts) ortension: 0.4(Chart.js) - Show data points on hover only (reduce visual clutter)
Bar Chart
Best for: comparisons between categories.
- Rounded top corners:
radius={[4, 4, 0, 0]} - Max bar width: 48px (prevent oversized bars on few data points)
- Add value labels above bars for small datasets (under 8 items)
Area Chart
Best for: volume/magnitude over time.
- Stack multiple areas for composition views
- Use decreasing opacity for stacked layers (0.6, 0.4, 0.2)
- Gradient fill from accent color to transparent
Pie / Donut Chart
Best for: part-to-whole relationships (max 6 segments).
- Donut preferred over pie (inner radius 60%)
- Active segment: slight outward offset on hover
- Center label: total value or primary metric
- Legend positioned below on mobile, right on desktop
Scatter Chart
Best for: correlation between two variables.
- Variable dot size for third dimension (bubble chart)
- Add quadrant lines if meaningful thresholds exist
- Tooltip shows both axis values plus label
Radar Chart
Best for: multi-dimensional comparison (3-8 axes).
- Filled area with low opacity (0.2)
- Grid lines match the dark theme
- Limit to 2-3 overlapping datasets for readability
Accessibility Requirements
Every chart must include:
-
ARIA attributes:
<div role="img" aria-label="Monthly revenue trend showing 23% growth from January to June 2025"> -
Reduced motion:
@media (prefers-reduced-motion: reduce) { .chart-container * { animation: none !important; transition: none !important; } } -
Color independence: Never rely on color alone to convey meaning. Add:
- Pattern fills or dash styles for line differentiation
- Direct labels or legends with text descriptions
- Shape variation for scatter plots
-
Screen reader fallback: Include a hidden data table:
<table className="sr-only"> <caption>Monthly revenue data</caption> {/* Full data in tabular form */} </table> -
Contrast: All text on the chart must meet WCAG AA (4.5:1 ratio against
zinc-900).
Responsive Behavior
- Charts fill their container width (
responsive: true/width="100%") - Set
min-h-[300px]on the wrapper to prevent CLS - On mobile (< 640px): move legends below the chart, reduce font sizes
- On print: switch to light theme with black text (via
@media print)
Performance
Charts are heavy — always lazy load:
import dynamic from 'next/dynamic';
const RevenueChart = dynamic(() => import('@/components/charts/RevenueChart'), {
loading: () => (
<div className="backdrop-blur-xl bg-white/5 border border-white/10 rounded-2xl p-6 min-h-[300px] animate-pulse" />
),
ssr: false,
});
- Never put chart components in the LCP critical path
- Use
ssr: false— canvas/SVG charts need the DOM - Skeleton matches the glass container dimensions exactly
Tooltip Styling
const CustomTooltip = ({ active, payload, label }) => {
if (!active || !payload) return null;
return (
<div className="backdrop-blur-xl bg-zinc-900/90 border border-white/10 rounded-lg px-3 py-2 shadow-xl">
<p className="text-zinc-400 text-xs mb-1">{label}</p>
{payload.map((entry, i) => (
<p key={i} className="text-zinc-100 text-sm font-medium">
<span style={{ color: entry.color }}>●</span> {entry.name}: {entry.value}
</p>
))}
</div>
);
};