data-visualization — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited data-visualization (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Degree of freedom: MIXED. Chart type and encoding [HIGH freedom]; existing-library/token probes and a11y fallback [LOW freedom — run exactly].
| Skill | Owns |
|---|---|
| data-visualization (this) | Interactive, accessible charts and dashboards with Recharts, D3, or Victory |
design-generative-art | Algorithmic visuals with seeded randomness and interactive controls |
Observe:package.jsonhas recharts; nosrc/components/charts; tokens define--primary. Interpret: reuse Recharts + tokens, not D3. Classify:ResponsiveContainerline chart + sr-only table; skeleton while loading. Verify: resizes; screen-reader table present; no second chart library.
package.json; did not add a second chart libraryaudit-analytics; empty/loading frames → audit-ui-statesCreate 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.
Before creating ANY visualization, verify:
cat package.json | grep -i "recharts\|chart\|d3\|visx\|nivo\|tremor"
rg "LineChart|BarChart|PieChart" --type tsx -l | head -10ls -la src/components/charts/ src/components/dashboard/ 2>/dev/null
rg "ResponsiveContainer|Chart" --type tsx | head -10cat tailwind.config.* | grep -A10 "chart\|colors"Why: Use existing chart library and styling conventions.
ResponsiveContainer width="100%"; set a fixed height.hsl(var(--primary)), fill-muted-foreground, stroke-muted; no hard-coded hex.tickLine={false} axisLine={false}); format Y ticks ($, %).Tooltip content uses bg-background border rounded-lg so it matches the theme.radius={[4,4,0,0]}, highlight the latest Cell), area (gradient <defs> fill), donut (innerRadius/outerRadius + center Label for the total).'use client'
import { LineChart, Line, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts'
export function RevenueChart({ data }: { data: { month: string; revenue: number }[] }) {
return (
<ResponsiveContainer width="100%" height={350}>
<LineChart data={data}>
<XAxis dataKey="month" tickLine={false} axisLine={false} className="text-xs fill-muted-foreground" />
<YAxis tickLine={false} axisLine={false} tickFormatter={(v) => `$${v}`} />
<Tooltip cursor={{ stroke: 'hsl(var(--muted))' }} />
<Line type="monotone" dataKey="revenue" stroke="hsl(var(--primary))" strokeWidth={2} activeDot={{ r: 6 }} />
</LineChart>
</ResponsiveContainer>
)
}Full line chart with custom tooltip, bar, area-gradient and donut components: references/recharts.md §Line chart, §Bar chart, §Area chart, §Pie/Donut chart.
LineChart with one Line, dot={false}, strokeWidth={1.5}, no axes, height ~40.number[] to { index, value } inside the component so callers pass plain arrays.w-20 slot beside the stat value, never as its own card.Sparkline component and stats-card usage: references/recharts.md §Sparklines.
rounded-xl border bg-card p-6; title in text-muted-foreground, value text-3xl font-semibold.Math.abs(change)%; green for up, red for down; pass the same color to the sparkline.sparklineData is optional; render the sparkline only when present.StatCard component: references/recharts.md §Stat cards with trends.
setData(prev => [...prev, point].slice(-N)); keep a bounded window (about 20 points).isAnimationActive={false} and a fixed YAxis domain so the chart does not jump per tick.RealtimeChart component: references/recharts.md §Real-time data updates.
ref'd <svg> from useEffect; svg.selectAll('*').remove() before redrawing on data change.scaleBand for categories, scaleLinear().nice() for values; size from clientWidth/clientHeight, with margins..transition().duration(750) from the baseline; color with the same hsl(var(--primary)) tokens.Full D3 bar chart component: references/d3-and-accessibility.md §D3.js custom bar chart.
<div role="img" aria-label="..."> that states what the chart shows and its range.aria-hidden="true"; provide the data as an sr-only <table> with a <caption>.<div role="img" aria-label="Revenue chart showing monthly data from January to December">
<ResponsiveContainer><LineChart data={data} aria-hidden="true">{/* ... */}</LineChart></ResponsiveContainer>
<table className="sr-only">
<caption>Monthly Revenue Data</caption>
<thead><tr><th>Month</th><th>Revenue</th></tr></thead>
<tbody>{data.map((d) => <tr key={d.month}><td>{d.month}</td><td>${d.revenue}</td></tr>)}</tbody>
</table>
</div>Full accessibility block: references/d3-and-accessibility.md §Accessibility.
After creating visualizations:
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.