chart-builder

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.

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

ScenarioLibraryReason
Standalone chart, no ReactChart.jsLightweight, canvas-based
React/Next.js projectRechartsNative React components, composable
Complex interactive dashboardsRechartsBetter state management integration
Static image export neededChart.jsCanvas 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:

IndexNameHexUse case
0Blue#3B82F6Primary metric
1Purple#8B5CF6Secondary metric
2Emerald#10B981Positive/growth
3Amber#F59E0BWarning/attention
4Rose#F43F5ENegative/decline
5Cyan#06B6D4Tertiary 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 (linearGradient from accent to transparent)
  • Smooth curves: type="monotone" (Recharts) or tension: 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:

  1. ARIA attributes:

    <div role="img" aria-label="Monthly revenue trend showing 23% growth from January to June 2025">
    
  2. Reduced motion:

    @media (prefers-reduced-motion: reduce) {
      .chart-container * {
        animation: none !important;
        transition: none !important;
      }
    }
    
  3. 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
  4. Screen reader fallback: Include a hidden data table:

    <table className="sr-only">
      <caption>Monthly revenue data</caption>
      {/* Full data in tabular form */}
    </table>
    
  5. 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>
  );
};