Install
$ agentstack add skill-medy-gribkov-arcana-data-visualization ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Data Visualization
Build accessible, performant data visualizations using modern libraries. Choose the right chart type, implement responsive layouts, and optimize for large datasets.
Chart Type Selection
Choose charts based on data relationships, not aesthetics.
BAD - Wrong chart for the task:
// Pie chart for comparing 12 categories - hard to compare angles
// Line chart for categorical data with no time relationship
GOOD - Chart matches data structure:
// Bar chart for category comparison - easy to compare lengths
// Scatter plot for correlation analysis
Chart selection guide:
- Bar/Column: Compare categories, rankings, discrete values
- Line: Time series, trends over continuous periods
- Scatter: Correlation between two variables, clustering
- Heatmap: Patterns in 2D categorical data (day/hour traffic)
- Area: Cumulative values over time, part-to-whole relationships
- Avoid pie charts: Use bar charts instead (easier comparison)
Recharts Implementation
Recharts provides React-native declarative charts with built-in responsiveness.
BAD - Fixed dimensions, no accessibility:
function SalesChart({ data }) {
return (
);
}
GOOD - Responsive, accessible, properly labeled:
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts';
interface DataPoint {
date: string;
sales: number;
profit: number;
}
interface SalesChartProps {
data: DataPoint[];
}
const SalesChart: React.FC = ({ data }) => {
return (
`$${value.toLocaleString()}`}
/>
);
};
Colorblind-Safe Palettes
Rainbow colors fail for 8% of men. Use palettes designed for accessibility.
BAD - Rainbow spectrum, red/green for critical info:
const COLORS = ['#ff0000', '#00ff00', '#0000ff', '#ffff00', '#ff00ff'];
// Red/green for positive/negative - invisible to deuteranopes
0 ? '#00ff00' : '#ff0000'} />
GOOD - Colorblind-safe palette with patterns:
// Paul Tol's colorblind-safe palette
const COLOR_PALETTE = {
blue: '#4477AA',
cyan: '#66CCEE',
green: '#228833',
yellow: '#CCBB44',
red: '#EE6677',
purple: '#AA3377',
grey: '#BBBBBB',
};
// Use blue/orange for diverging data (safe for all types of colorblindness)
const getDivergingColor = (value: number): string => {
return value > 0 ? COLOR_PALETTE.blue : COLOR_PALETTE.red;
};
// Add patterns for critical distinctions
{value
}
Recommended palettes:
- Categorical (up to 7): Paul Tol's bright scheme
- Sequential: Single hue progression (light blue → dark blue)
- Diverging: Blue → white → orange (not red → green)
Performance with Large Datasets
Canvas outperforms SVG for 1000+ points. Use data windowing and memoization.
BAD - Re-render entire chart on every update:
function LiveChart() {
const [data, setData] = useState([]);
useEffect(() => {
const interval = setInterval(() => {
setData(prev => [...prev, fetchNewPoint()]); // Unbounded growth
}, 1000);
return () => clearInterval(interval);
}, []);
return ;
}
GOOD - Windowed data, memoized component, canvas rendering:
import { useMemo, useCallback } from 'react';
import { Line } from 'react-chartjs-2';
import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement } from 'chart.js';
ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement);
interface LiveChartProps {
maxPoints?: number;
}
const LiveChart: React.FC = ({ maxPoints = 100 }) => {
const [data, setData] = useState([]);
// Keep only last N points
const addDataPoint = useCallback((point: DataPoint) => {
setData(prev => {
const updated = [...prev, point];
return updated.slice(-maxPoints);
});
}, [maxPoints]);
useEffect(() => {
const interval = setInterval(() => {
addDataPoint(fetchNewPoint());
}, 1000);
return () => clearInterval(interval);
}, [addDataPoint]);
// Memoize chart config to prevent re-creation
const chartData = useMemo(() => ({
labels: data.map(d => d.timestamp),
datasets: [{
label: 'Live Value',
data: data.map(d => d.value),
borderColor: '#d4943a',
borderWidth: 2,
pointRadius: 0, // No dots for performance
}],
}), [data]);
const options = useMemo(() => ({
responsive: true,
maintainAspectRatio: false,
animation: false, // Disable animations for real-time
plugins: {
legend: { display: false },
},
scales: {
x: { display: false }, // Hide labels for performance
y: { beginAtZero: true },
},
}), []);
return (
);
};
Performance tips:
- Canvas vs SVG: Use Chart.js (canvas) for 1000+ points, Recharts (SVG) for
**GOOD - Responsive grid with visual hierarchy:**
```tsx
import { ResponsiveContainer } from 'recharts';
const Dashboard: React.FC = () => {
return (
{/* Primary metric - full width */}
Revenue Trend
{/* ... */}
{/* Secondary metrics - 2 columns on desktop */}
User Growth
{/* ... */}
Conversion Rate
{/* ... */}
{/* Tertiary - heatmap spans 2 columns */}
Activity Heatmap
{/* ... */}
);
};
D3.js for Custom Visualizations
Use D3 for specialized charts not available in Recharts/Chart.js.
BAD - Manipulate DOM directly in React:
function CustomChart() {
useEffect(() => {
d3.select('#chart').append('svg'); // React loses control
}, []);
return ;
}
GOOD - Use refs, let React manage DOM:
import { useRef, useEffect } from 'react';
import * as d3 from 'd3';
interface Node {
id: string;
value: number;
}
interface Link {
source: string;
target: string;
}
interface NetworkGraphProps {
nodes: Node[];
links: Link[];
}
const NetworkGraph: React.FC = ({ nodes, links }) => {
const svgRef = useRef(null);
useEffect(() => {
if (!svgRef.current) return;
const svg = d3.select(svgRef.current);
const width = 600;
const height = 400;
// Clear previous render
svg.selectAll('*').remove();
const simulation = d3.forceSimulation(nodes)
.force('link', d3.forceLink(links).id((d: any) => d.id))
.force('charge', d3.forceManyBody().strength(-200))
.force('center', d3.forceCenter(width / 2, height / 2));
const link = svg.append('g')
.selectAll('line')
.data(links)
.join('line')
.attr('stroke', '#999')
.attr('stroke-width', 2);
const node = svg.append('g')
.selectAll('circle')
.data(nodes)
.join('circle')
.attr('r', (d) => Math.sqrt(d.value) * 5)
.attr('fill', '#d4943a')
.attr('aria-label', (d) => `Node ${d.id}, value ${d.value}`);
simulation.on('tick', () => {
link
.attr('x1', (d: any) => d.source.x)
.attr('y1', (d: any) => d.source.y)
.attr('x2', (d: any) => d.target.x)
.attr('y2', (d: any) => d.target.y);
node
.attr('cx', (d: any) => d.x)
.attr('cy', (d: any) => d.y);
});
return () => {
simulation.stop();
};
}, [nodes, links]);
return (
);
};
Accessibility
Screen readers need context. Provide labels, summaries, and data tables.
BAD - No semantic info, colors only:
GOOD - ARIA labels, semantic HTML, text alternatives:
import { BarChart, Bar, XAxis, YAxis } from 'recharts';
const AccessibleChart: React.FC = ({ data }) => {
const maxValue = Math.max(...data.map(d => d.value));
const minValue = Math.min(...data.map(d => d.value));
return (
Sales by quarter: Q1 ${data[0].value}, Q2 ${data[1].value}, Q3 ${data[2].value}, Q4 ${data[3].value}.
Highest quarter: {data.find(d => d.value === maxValue)?.quarter}.
[`$${value.toLocaleString()}`, 'Sales']}
/>
{/* Provide data table for screen readers */}
View data table
Quarter
Sales
{data.map(d => (
{d.quarter}
${d.value.toLocaleString()}
))}
);
};
Accessibility checklist:
- Add
role="img"andaria-labelto charts - Use `
and` for semantic structure - Provide text summary in
.sr-onlyclass - Include data table alternative
- Label axes and provide units
- Use patterns or textures in addition to color
- Ensure 4.5:1 contrast ratio for text on charts
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: medy-gribkov
- Source: medy-gribkov/arcana
- License: Apache-2.0
- Homepage: https://www.npmjs.com/package/@sporesec/arcana
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.