anthropics/graphing
Compose polished charts (timeseries, bar, line, area, pie, scatter, or anything else the data calls for) from tabular data using the chartkit primitives, producing PNG, SVG, or self-contained interactive HTML. Use when the user asks to chart, graph, plot, or visualize data and wants something better than raw matplotlib defaults.
npx skills add https://github.com/anthropics/claude-tag-plugins --skill graphing
You write the plotting code. The kit provides the parts that should stay consistent across every chart: typography, color derivation from the background, the title and caption frame, and offline HTML packaging. Everything about the chart itself is your judgement applied to the data in front of you.
Import chartkit by putting this skill's scripts/ directory on sys.path. Use its absolute path, not a relative path, since your working directory is the user's project. The examples below write it as /path/to/graphing/scripts; substitute the real path.
Relevant primitives:
theme(bg, font): sets matplotlib rcParams and returns resolved colors. Foreground colors derive from background luminance, so a dark bg produces a correct dark chart. Fields: bg dark text muted grid spine accent secondary series font_css.palette(n, base): n colors. No base cycles the default series, a hex base builds a ramp from it, a list cycles the list.finish(ax, title, subtitle, source): The typographic frame. Left-aligned bold title, muted subtitle, small provenance caption.save(fig, stem, formats, dpi): Writes stem.png, stem.svg, or both. Returns the paths.write_html(out, data, component_js, title, bg, font): Self-contained interactive page. Inlines React, ReactDOM, react-is, and Recharts from third_party/ so the file opens offline. Your component reads window.__CHART_DATA__ and renders into #root.zero_fill_days(pairs) rolling_mean(values, w) log_floor(values): Small data helpers for the gotchas listed below. Use them only when they fit.theme(bg=...) and brand colors to palette(base=...) or directly. Fall back to the defaults only when nothing is inferable. The font defaults to Inter and only changes when the user names a typeface or the destination has a documented brand font.finish, and saves.PNG or SVG via matplotlib:
import sys
sys.path.insert(0, "/path/to/graphing/scripts")
import chartkit as ck
import matplotlib.pyplot as plt
c = ck.theme() # or theme(bg="#0f1419") for a dark surface
fig, ax = plt.subplots(figsize=(10, 5))
# ...plain matplotlib against the data, using c.accent, c.secondary, ck.palette(...)
ck.finish(ax, title="What the chart shows", subtitle="scope or time range",
source="source: where the data came from, date")
print(ck.save(fig, "out/report", formats=("png", "svg")))
Interactive HTML via Recharts, written as plain React.createElement with no JSX and no build step:
component = """
(function () {
var e = React.createElement
var R = window.Recharts
var DATA = window.__CHART_DATA__
function App() {
return e(R.ResponsiveContainer, { width: "100%", height: 420 },
e(R.LineChart, { data: DATA },
e(R.CartesianGrid, { stroke: "GRID", vertical: false }),
e(R.XAxis, { dataKey: "x" }), e(R.YAxis, null),
e(R.Tooltip, null),
e(R.Line, { dataKey: "y", stroke: "ACCENT", strokeWidth: 2.4,
dot: false, isAnimationActive: false })))
}
ReactDOM.createRoot(document.getElementById("root")).render(e(App))
})()
"""
ck.write_html("out/report.html", data, component, title="What the chart shows")
Substitute real colors from the theme into the component and keep isAnimationActive: false so the chart is complete the moment the file opens.
These are the defaults of good charts. Deviate when the data argues for it.
zero_fill_days fills them with zero. Skip it when zeros would be the lie (sparse sampling rather than absence of events).rolling_mean is trailing, early points average what exists so far. Do not center it on data that ends today.log_floor gives a lower bound one decade down.domain on the YAxis when data is negative or log scaled.Run a minimal render and look at it.
python3 -c "
import sys
sys.path.insert(0, '/path/to/graphing/scripts')
import chartkit as ck
import matplotlib.pyplot as plt
c = ck.theme()
fig, ax = plt.subplots(figsize=(8, 4))
ax.bar(['A', 'B', 'C'], [10, 30, 20], color=c.accent, width=0.5)
ck.finish(ax, title='chartkit smoke test')
print(ck.save(fig, '/tmp/ck_smoke'))
"
Confirm a non-empty PNG is written, then read it back and check it renders correctly.
Take anthropics/graphing from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.