| name | data-viz |
| description | Create animated data visualizations and charts as native SVG with no external libraries. Use this skill when the user asks to "create a chart", "build a graph", "visualize data", "make a bar chart", "create a line chart", "build a pie chart", "show growth", "plot this data", or mentions charts, graphs, data visualization, metrics, dashboards, or analytics. |
| version | 1.0.0 |
Data Viz
Create publication-quality animated data visualizations as native SVG with vanilla JavaScript. No D3, no Chart.js — hand-crafted for full control over styling, animation, and presentation readability.
Interactive Workflow
Before generating, ask the user:
- What data? (paste raw numbers, describe the dataset, or point to a file)
- What chart type? Suggest the best type based on the data:
- Line chart — trends over time (growth curves, metrics)
- Bar chart — comparisons between categories
- Horizontal bar — rankings, long category labels
- Pie/donut — proportions of a whole (use sparingly)
- Area chart — cumulative volumes over time
- Scatter plot — correlation between two variables
- Gauge/meter — single metric against a target
- Context? (standalone page, embedded in a presentation, part of a document)
- Theme?
Present theme options:
Light Themes
- Warm Paper — Cream background, muted grid, dark ink labels
- Clean White — White background, blue data lines, minimal grid
Dark Themes
- Dashboard — Dark (#1a1a1a), vibrant data colors, glowing tooltips
- Midnight — Navy (#0f172a), cyan/purple data lines
- Animated? (yes for presentations, optional for documents)
Core Principles
Build Natively
Always build charts as inline SVG with optional vanilla JS for animation. Native SVG charts:
- Animate smoothly with
requestAnimationFrame
- Match the parent document's design system
- Scale cleanly at any resolution
- Support hover tooltips without library overhead
- Print cleanly
Cumulative for Growth
When showing growth over time (user signups, revenue, downloads), ALWAYS use cumulative curves. Daily/weekly data looks spiky; cumulative bends upward (the "hockey stick").
Honest Proportions
- Space dates proportionally to real time on the x-axis — gaps where nothing happened appear as flat stretches
- Never even-space dates that are unevenly distributed
- Start y-axis at 0 for bar charts (truncated axes mislead)
- Line charts may start above 0 when the range would compress meaningful variation
Presentation-Scale Tooltips
Tooltips for presentations must be 2-3x larger than desktop web:
- Minimum 18px bold text
- Opaque background that fully masks content behind
- Reposition near viewport edges (bottom-left for rightmost points)
- Use CSS-only approach when possible (adjacent sibling selector)
Chart Architecture
SVG Structure
<svg viewBox="0 0 900 500" xmlns="http://www.w3.org/2000/svg">
<defs>
<style></style>
<linearGradient id="areaGrad">...</linearGradient>
<clipPath id="chartClip">
<rect id="clipRect" x="0" y="0" width="0" height="400" />
</clipPath>
</defs>
<g class="grid">...</g>
<g class="x-axis" transform="translate(80, 420)">...</g>
<g class="y-axis" transform="translate(80, 20)">...
...
Axis Layout
- Left padding: 80px (y-axis labels)
- Bottom padding: 80px (x-axis labels)
- Right padding: 40px
- Top padding: 20px
- Chart area: remaining space within viewBox
Grid Lines
Subtle horizontal lines only (no vertical grid for most charts):
<line x1="80" y1="100" x2="860" y2="100" stroke="#e0e0e0" stroke-width="0.5" />
Animation Patterns
Chart Reveal with Clip-Rect
Animate a <clipPath> rect width from 0 to full width:
function animateChart(clipRect, fullWidth, duration = 2000) {
const start = performance.now();
function update(now) {
const t = Math.min((now - start) / duration, 1);
const eased = 1 - Math.pow(1 - t, 2);
clipRect.setAttribute('width', eased * fullWidth);
if (t < 1) requestAnimationFrame(update);
}
requestAnimationFrame(update);
}
Critical: CSS @keyframes cannot animate SVG geometric attributes (width, height, x, y) cross-browser. Always use JavaScript requestAnimationFrame + setAttribute().
Synchronized Counter + Chart
When a headline number (e.g., "48 users") accompanies a chart, animate both in a single loop:
function animateDataSlide(counterEl, clipRect, target, chartWidth) {
const start = performance.now();
const duration = 2000;
function update(now) {
const t = Math.min((now - start) / duration, 1);
const eased = 1 - Math.pow(1 - t, 2);
counterEl.textContent = Math.round(eased * target);
clipRect.setAttribute('width', eased * chartWidth);
if (t < 1) requestAnimationFrame(update);
}
requestAnimationFrame(update);
}
Easing: Ease-Out Quadratic
1 - (1-t)^2 — the counter decelerates near the end so the final number "lingers" and the audience catches it. Better than linear (boring) or ease-in (big number flashes past too fast).
Remove Redundant Labels
When an animated counter shows the total, remove any static label showing the same number. Redundancy creates visual noise and z-index layering bugs.
Reset on Navigation (Reveal.js)
When embedded in a presentation:
Reveal.on('slidechanged', event => {
if (event.currentSlide === dataSlide) {
animateDataSlide(counter, clipRect, 48, 800);
} else if (event.previousSlide === dataSlide) {
counter.textContent = '0';
clipRect.setAttribute('width', 0);
}
});
CSS-Only Tooltips
<circle class="chart-dot" cx="400" cy="150" r="6" fill="#0891b2" />
<g class="chart-tip" transform="translate(400, 120)">
<rect x="-55" y="-32" width="110" height="32" rx="8" fill="#1a1a1a" />
<text x="0" y="-12" text-anchor="middle" fill="#fff"
font-family="JetBrains Mono" font-size="18" font-weight="700">Mar 12: 48</text>
</g>
.chart-tip { opacity: 0; pointer-events: none; transition: opacity 0.15s; }
.chart-dot:hover + .chart-tip { opacity: 1; }
.chart-dot { cursor: pointer; transition: r 0.15s; }
.chart-dot:hover { r: 10; }
Data Processing
When the user provides raw data:
- Parse dates and values
- Compute cumulative totals if showing growth
- Calculate proportional x-positions based on actual date gaps
- Scale y-values to fit the chart area
- Generate SVG path
d attribute: M x1,y1 L x2,y2 L x3,y3...
- Generate area fill path: line path +
L lastX,maxY L firstX,maxY Z
Additional Resources
For detailed chart patterns, consult:
references/chart-patterns.md — Specific SVG patterns for each chart type