graphein-visuals
Skill108 starsChanged 5 months agoMITView on GitHub
Use when adding a chart, graph, plot, KPI, table, or any data visualization to this app. Charts are authored as Graphein specs — one chart = one JSON `ChartSpec` (a `type`, a tidy `data` array, and an `encoding` that names the columns) rendered by the bundled `<Chart spec={…} />` component (`@/components/Chart`). Covers the spec model, the chart-type catalog (line/area/bar/scatter/pie/heatmap/histogram/combo/funnel/treemap/waterfall/ gauge/bullet/calendarHeatmap/slope/dumbbell/table/matrix/box/sankey/choropleth), encodings and field types, in-spec transforms/annotations/trendline/facet, value formatting, cross-chart selections, `validateSpec`, and gotchas. Triggers: chart, graph, plot, visualize, dashboard, bar chart, line chart, pie, scatter, heatmap, KPI, metric, table, Graphein, spec, dataviz.
We split this skill into the rules it gives an agent
2 rules. About 0 are common, 2 are distinctive, and 0 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
0
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 218
Before shipping a spec, sanity-check it:
- Distinctive: line 243
No custom-chart escape hatch — re-express with the closest supported type.
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Graphein visuals — author a spec, drop it in <Chart>
One chart is one JSON object. You don't hand-write SVG or wire a charting
library. You (1) shape your data into plain rows, (2) author a single Graphein
ChartSpec
— a type, a tidy data array, and an encoding that names the columns — and
(3) render it with the bundled <Chart> component. Graphein owns axes, scales,
ticks, color, the legend, tooltips, number/date formatting, responsive sizing,
animation, and dark mode.
grapheinis a framework-agnostic canvas engine (no React entry of its own), so this app ships a thin binding atsrc/components/Chart.tsx(+useChart.ts). Always render through<Chart>— don't callrender()fromgrapheindirectly in components.
Fast path
import { Chart } from '@/components/Chart';
import type { ChartSpec } from 'graphein';
const spec: ChartSpec = {
type: 'bar',
title: 'Revenue by region',
data: [
{ region: 'North', revenue: 4200 },
{ region: 'South', revenue: 3100 },
{ region: 'East', revenue: 5300 },
{ region: 'West', revenue: 2600 },
],
encoding: {
x: { field: 'region', type: 'nominal' },
y: { field: 'revenue', type: 'quantitative', format: '$,.0f' },
},
};
export function RevenueCard() {
// The chart fills its parent, so give the parent an explicit size.
return (
<div style={{ width: '100%', height: 360 }}>
<Chart spec={spec} />
</div>
);
}Size the parent. <Chart> fills its container (width/height: 100%); it
draws nothing in a zero-height box. Wrap it in an element with a real height
(fixed px, a grid/flex track, or 100% of a sized ancestor).
Keep the spec stable. Pass a memoized or module-constant spec. A brand-new
object every render replays the entrance animation — use useMemo when the spec
depends on props/state.
Shape data as a tidy table
Graphein expects long/tidy data: one row per observation, one column per
variable. The same table drives every chart — point different channels at
columns. To compare groups, add a series channel — don't pre-pivot into
one column per group.
// Good (tidy): one row per month × region
[{ month: '2024-01', region: 'North', revenue: 4200 },
{ month: '2024-01', region: 'South', revenue: 3100 }]
// encoding: x=month, y=revenue, series=region → multi-series line/barRows are plain JSON — no functions, no DOM nodes, no Date objects. For time
axes, pass ISO strings ("2024-01", "2024-01-15") or epoch ms and set
type: "temporal".
Encoding & field definitions
Cartesian charts (line/area/bar/scatter) plus pie/heatmap/funnel/
treemap/waterfall/calendarHeatmap/dumbbell/slope/combo map columns
onto channels via encoding:
| Channel | Used by | Purpose |
x | line, area, bar, scatter, heatmap, histogram, combo, slope, calendarHeatmap | Horizontal position / bin field |
y | line, area, bar, scatter, heatmap, slope | Vertical position |
series | line, area, bar, slope | Split into multiple series |
size | scatter | Bubble radius |
color | heatmap, pie, treemap, calendarHeatmap | Continuous color or category |
theta | pie | Slice value |
stage / value | funnel, waterfall | Ordered stage / stage value |
category / value / group | treemap, dumbbell | Leaf identity / measure / parent |
date | calendarHeatmap | Date field → one cell per day |
FieldDef = { field, type?, aggregate?, title?, format?, scale? }:
field(required) — column name; dotted paths (a.b) read nested values.type—quantitative | temporal | ordinal | nominal(inferred when omitted; set it explicitly for time axes and to avoid surprises).format— a format hint for labels / ticks / tooltips.aggregate—sum | mean | avg | min | max | count | countDistinct | median | first | lastwhen grouping.
gauge and bullet use a top-level value: { field } instead of encoding.
Chart-type catalog
Pick the closest type; there is no custom-chart escape hatch — re-express exotic shapes with the nearest supported type.
- line / area —
x,y, optionalseries. line:points?,area?,curve?. area:stack?(totals).curve:linear|monotone|step|stepBefore| stepAfter|catmullRom. - bar —
x,y, optionalseries.stack?(else side-by-side groups),cornerRadius?,orientation?: 'vertical'|'horizontal'(horizontal swaps axes; keepx=category,y=value). For top-N, sort rows by value. - scatter —
x,y, optionalsize(bubbles),series(colored groups). - pie / donut —
theta(value) +color(category).donut?: true | 0..1inner-radius ratio.labels?. - heatmap —
x,y(categories) +color(measure).scheme?: blues|teal|viridis|magma|greys. - histogram —
x(the numeric measure); auto-bins.bin?: { maxbins?, step? }. - combo (dual-axis) — shared
x+layers: [{ mark:'bar'|'line'|'area', axis:'left'|'right', encoding:{ y } }]for measures on independent scales. - funnel —
stage+value.percent?: 'first'|'previous',labels?. - treemap —
category+value, optionalgroup+color. - waterfall —
stage+value(signed).totals?: string[]. - gauge / bullet — top-level
value: { field }. gauge:min?,max?. bullet:target?: { field }+encoding.label. - calendarHeatmap —
date+color.scheme?. - slope —
x(two values),y,series. - dumbbell —
category+value+group(2+ points per category). - table —
columns: TableColumn[](field, title, format, align, width, sortable, conditionalFormat, total, …),totals?,density?. - matrix (pivot) —
rows,columns,values: [{ field, op, label?, format?, conditionalFormat?, showAs? }],subtotals?,grandTotals?. - box / sankey / choropleth — distributions / flows (
source+target+value) / geography (geo+key+color). Rare; see the library docs.
In-spec features (declarative, on BaseSpec)
Reshape and enrich a spec without touching your data pipeline:
transform: Transform[]—aggregate,bin,filter,fold,timeUnit,calculaterun before the chart builds.annotations: Annotation[]— reference lines/bands/zones + point callouts, e.g. a target line:annotations: [{ type: 'line', axis: 'y', value: 5000, label: 'Target' }].insights: true— auto-mark the max & min (opt intooutliers).trendline: true— linear line of best fit (line/scatter).facet: { field, columns? }— small-multiples trellis, one panel per category.title,legend,tooltip,axes,animation,description(a11y).
Value formatting
Format inside the spec (never pre-format numbers to strings). A subset of d3-format for numbers, strftime for dates:
| Hint | Input → Output |
,d | 1234567 → 1,234,567 |
.1f | 3.14159 → 3.1 |
.0% | 0.42 → 42% |
$,.0f | 5230 → $5,230 |
.2s | 1234567 → 1.2M |
Dates: any hint with % is a date pattern — %b %e, %Y → Jan 2, 2024
(%Y/%y year, %m month, %d/%e day, %B/%b month name, %H:%M time).
Theming
This template has no design-token system, so charts render with Graphein's
built-in theme (teal accent, slate neutrals, a 10-hue categorical palette) and
it is dark-mode aware. To recolor, set spec.theme (a ThemeInput) — e.g.
theme: { colors: { accent: '#7c3aed' } } — or pass a palette. Keep colors in
the spec; don't hand-paint marks.
Interactivity (cross-chart selections)
Charts can publish/consume named selections. Create one shared bus and pass the
same store to several <Chart>s to cross-highlight / cross-filter:
import { useMemo } from 'react';
import { createSelectionStore } from 'graphein';
import { Chart } from '@/components/Chart';
const store = useMemo(() => createSelectionStore(), []);
// Publisher: clicking a bar sets the "pick" selection
<Chart store={store} spec={{ type: 'bar', data: rows,
params: [{ name: 'pick', select: { type: 'point', fields: ['region'] } }],
encoding: { x: { field: 'region' }, y: { field: 'revenue' } } }} />
// Consumer: dims non-matching rows when "pick" changes
<Chart store={store} spec={{ type: 'line', data: rows, highlight: { param: 'pick' },
encoding: { x: { field: 'month', type: 'temporal' },
y: { field: 'revenue' }, series: { field: 'region' } } }} />filter clauses ({ param }, { field, equals|oneOf|range|contains }) subset
rows. Observe changes via <Chart onSelectionChange={(name, value) => …} />.
Validate & self-check
Before shipping a spec, sanity-check it:
import { validateSpec } from 'graphein';
const { valid, errors, warnings } = validateSpec(spec);Common failures validateSpec catches:
- Missing
encodingfor the type (line/area/bar/scatterneedx+y;pieneedstheta+color;heatmapneedsx+y+color;funnelneedsstage+value). - A
fieldthat doesn't exist in the data rows (a typo silently drops the channel). - Pre-pivoted / wide data — pass tidy rows and split with
series.
Empty data renders an empty chart — show a real empty/loading state in the UI
rather than shipping placeholder rows.
Gotchas
- Render through
<Chart>, notrender()— the binding handles mount/update/ teardown and is StrictMode-safe. - Give the chart's parent an explicit height, or it collapses to nothing.
- Memoize specs built from props/state so they don't re-animate every render.
- No custom-chart escape hatch — re-express with the closest supported type.
- Everything is plain JSON: no functions, no DOM nodes, no
Dateobjects.
In this repository33 other files it gives its agents.
- AGENTS.mdAGENTS.md
- templates/angular-blankapp/AGENTS.mdAGENTS.md
- templates/angular-dashboard/AGENTS.mdAGENTS.md
- templates/campus-scheduler/AGENTS.mdAGENTS.md
- templates/fabric-atlas/AGENTS.mdAGENTS.md
- templates/field-technician/AGENTS.mdAGENTS.md
- templates/finance-analytics/AGENTS.mdAGENTS.md
- templates/ibcs-trainer/AGENTS.mdAGENTS.md
- templates/maritime-insights/AGENTS.mdAGENTS.md
- templates/paragliding-insights/AGENTS.mdAGENTS.md
- templates/slide-deck/AGENTS.mdAGENTS.md
- templates/static-blankapp/AGENTS.mdAGENTS.md
- templates/todo-local-experimental/AGENTS.mdAGENTS.md
- templates/universal-app/AGENTS.mdAGENTS.md
- template-gallerySkill
- rayfinSkill
- rayfinSkill
- rayfinSkill
- rayfinSkill
- rayfinSkill
- rayfinSkill
- rayfinSkill
- rayfinSkill
- analyticsSkill
- app-designSkill
- authenticationSkill
- build-workflowSkill
- capability-routerSkill
- data-modelingSkill
- daxSkill
- fabric-dataSkill
- headless-previewSkill
- visualsSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

