dataviz-craft

$npx mdskill add QinghongLin/data2story-skill/dataviz-craft

Provides editorial-grade chart selection and Vega-Lite craft rules.

  • Helps choose the right chart type for a given analytical intent.
  • Depends on FT Visual Vocabulary, Cleveland-McGill ordering, and bbplot rules.
  • Ranks chart types by intent and applies de-clutter and encoding rules.
  • Outputs Vega-Lite recipes or D3 fallback specs for implementation.

SKILL.md

.github/skills/dataviz-craftView on GitHub ↗
---
name: dataviz-craft
description: "A shared reference library for editorial-grade data-visualization craft — Vega-Lite-first with a D3 fallback for charts Vega-Lite can't express. Read by the Designer at chart selection (intent → ranked chart type), the Programmer at implementation (editorial Vega-Lite recipes, annotation layers, axis/label de-clutter, encoding craft), and the Auditor/Critic for chart-quality review. It encodes the FT Visual Vocabulary intent taxonomy, the Cleveland–McGill channel-accuracy ordering, the BBC bbplot de-clutter ruleset, and colorblind-safe encoding rules. Not a pipeline stage — a craft source, like frontend-design."
allowed-tools: Read
---

# Dataviz Craft

A shared library for choosing and building **charts that carry one idea cleanly**. This is not a pipeline stage; it is a reference the **Designer** reads to pick the right chart type for the message, the **Programmer** reads to build it as an editorial Vega-Lite spec, and the **Auditor/Critic** reads to judge whether a chart is honest and legible.

It is the chart-craft counterpart to `frontend-design` (page/visual identity). For color scales it does **not** redefine anything — it cross-references `frontend-design/references/design_tokens.json` `data_color_scales`. For Vega-Lite mechanics (mount/centering/`width:container`/`scale.zero`/`labelExpr`) it **extends** `data2story-pro/programmer/references/component_implementations.json`, it does not duplicate it.

## When to use

- **Designer**: before writing a `chart_spec` per section, name the message's **intent** (deviation / correlation / ranking / distribution / change-over-time / part-to-whole / magnitude / spatial / flow) and pick a ranked candidate from [`references/chart_chooser.json`](references/chart_chooser.json). Record the chart type + intent + what to highlight in `designer.json`. If the chosen type is tagged `vega_lite_native: false`, flag it for the D3 fallback.
- **Programmer**: implement the chosen type from [`references/vega_recipes.json`](references/vega_recipes.json) (editorial Vega-Lite skeleton), add reference/threshold/label layers from [`references/annotation_layers.json`](references/annotation_layers.json), apply the de-clutter config from [`references/axis_label_polish.json`](references/axis_label_polish.json), and obey [`references/encoding_craft.json`](references/encoding_craft.json) (area-not-radius, sort-by-value, no dual-axis, colorblind-safe). For a `native:false` type use [`references/d3_fallback_recipes.json`](references/d3_fallback_recipes.json).
- **Auditor / Critic**: review a chart against the same files — is the chart type right for the stated intent (chart_chooser), does the baseline lie (axis_label_polish zero-baseline rule), is the key datum annotated (annotation_layers), is the encoding accurate and colorblind-safe (encoding_craft, Cleveland–McGill ordering)?

## Core principles

1. **Vega-Lite-first.** Build every chart Vega-Lite can express as a Vega-Lite spec via Vega-Embed (CDN). Reach for D3 **only** for types Vega-Lite cannot express (sankey, treemap, chord, sunburst, force-network) — see [`references/d3_fallback_recipes.json`](references/d3_fallback_recipes.json). D3 is an allowed CDN, not the default.
2. **One chart, one idea.** Each chart answers one question. If a chart needs two sentences to explain, split it. Pick the chart type from the *message intent*, not from what looks impressive — [`references/chart_chooser.json`](references/chart_chooser.json).
3. **Annotate the key datum.** The reader should see the point being made without hunting. Label the line end, draw the threshold/reference line, highlight the one bar that matters with the accent — [`references/annotation_layers.json`](references/annotation_layers.json).
4. **De-clutter the frame.** Remove what doesn't carry information: minor gridlines, redundant axis lines/ticks/titles, legends when a direct label is clearer — [`references/axis_label_polish.json`](references/axis_label_polish.json).
5. **Right channel for the intent.** Prefer the most accurate visual channel the data allows: position > length > angle > area > color (Cleveland–McGill). Encode the key variable in the strongest available channel — [`references/encoding_craft.json`](references/encoding_craft.json), [`references/chart_chooser.json`](references/chart_chooser.json).
6. **Color is borrowed, never reinvented.** Use the story's `--accent` for the highlighted datum and mute the rest. For sequential/diverging/categorical scales, cross-reference [`frontend-design/references/design_tokens.json`](../frontend-design/references/design_tokens.json) `data_color_scales` — the same value means the same color across map, bars and callouts. Colorblind-safe rules live in [`references/encoding_craft.json`](references/encoding_craft.json).

## References

- **[`references/chart_chooser.json`](references/chart_chooser.json)** — the decision core: 9 message intents → ranked chart candidates, each tagged `vega_lite_native`; the purpose-first frame (comparison/composition/distribution/relationship/trend → narrow intent) and the Cleveland–McGill channel-accuracy hierarchy.
- **[`references/vega_recipes.json`](references/vega_recipes.json)** — per-type editorial Vega-Lite recipe skeletons for every native type (bar/grouped/stacked, line/area/layered, slope, dot/lollipop, facet small-multiples, heatmap, strip/beeswarm, connected-scatter, scatter/bubble, choropleth): mark, key encoding notes, highlight, editorial defaults.
- **[`references/annotation_layers.json`](references/annotation_layers.json)** — Vega-Lite annotation techniques: `rule` marks for threshold/reference lines and range bands, `text` + argmax for line-end labels instead of a legend, in-chart callout boxes, conditional highlight of the key datum.
- **[`references/axis_label_polish.json`](references/axis_label_polish.json)** — the BBC bbplot de-clutter ruleset as Vega-Lite config: gridline/tick/axis-line/title removal, legend on top, tick `format`/`labelExpr` ($/%/abbreviated, Vega-expr not JS), the zero-baseline rule, log-scale domain.
- **[`references/encoding_craft.json`](references/encoding_craft.json)** — beyond color: bubble area-not-radius, sort-by-value, the dual-axis warning, colorblind-safe palettes and redundant encoding. Cross-refs `design_tokens.json` `data_color_scales` for the actual scales.
- **[`references/d3_fallback_recipes.json`](references/d3_fallback_recipes.json)** — ONLY the non-native types (sankey, treemap, chord, sunburst, force-network): when to reach for D3, a recipe outline (D3 module + layout generator), and the SVG-mark performance ceiling.
- **[`../frontend-design/references/design_tokens.json`](../frontend-design/references/design_tokens.json)** — (cross-reference, not owned here) `data_color_scales` for sequential/diverging/categorical color.

A chart is well-crafted when its type matches the message intent, the baseline is honest, the one datum that matters is annotated, the frame carries no decoration, the strongest available channel encodes the key variable, and the colors mean the same thing everywhere and survive color blindness.

_license_note: This file encodes uncopyrightable methods and re-authored principles. Taxonomy and intent → chart mapping derive from the Financial Times Visual Vocabulary (method, re-authored — not FT prose). Channel-accuracy ordering from Cleveland & McGill (1984). De-clutter rules adapted from the BBC bbplot R package (MIT). Purpose-first framing and several encoding facts re-authored from rohitg00/data-visualization and chrisvoncsefalvay data-viz references. Vega-Lite mechanics extend the project's own component_implementations.json.

More from QinghongLin/data2story-skill

SkillDescription
analystExhaustively profile a dataset and list ALL possible analyses — distributions, correlations, rankings, trends, group comparisons, anomalies. Reads detective.json for context. Runs after the Detective/Scout, before the Editor. Outputs analyst.json with ana_xx IDs and chart-ready data_tables.
auditorAudit a generated Data2Story blog for build correctness across ALL modalities by ACTUALLY RENDERING it in a real headless browser (when available) — catching blank/0-width charts, broken/oversized media, desktop+mobile overflow, and dead interactions that source inspection cannot see — then having vision agents review the screenshots, playtesting every interactive, and walking the flagship capability contract. Plus VIEW every image for a wrong/AI-faked subject. Fixes layout in place; sends content-correctness problems back to the owning role. Use at Stage 6 after the Programmer assembles index.html and before the Critic; re-runs each revision round. Triggers: a built index.html exists, or you need to confirm charts/media/interactions actually render.
cinematographerDesign the MANDATORY cinematic scroll experience (every blog ships one): a full-bleed, scroll-driven background — primarily from the Scout's verified real imagery — plus the global motion choreography, so the narrative unfolds like a film as the reader scrolls. Owns the BACKGROUND layer + page-level motion (not the Designer's per-section visuals, not the Interaction centerpiece — it stages them inside the scroll). Outputs cinematographer.json (cin_xx scenes). There is no off path: a topic with no sourceable real imagery falls back to a generative-atmosphere or data-driven-spine background, never to a bare column.
copywriterName the piece — re-write the masthead (headline + standfirst + kicker), every section title, and every figure/photo/table caption to a research-driven titling standard, killing the AI-tell patterns (the 'flat statement. flat counter-statement.' two-beat above all) a competent default falls into. Reads editor.md/json + analyst.json + the resolved topic_profile; writes copywriter.json — STRINGS ONLY (masthead{headline,standfirst,kicker}, items{edt_xx:{title}, des_xx:{caption}}), each backed by a real ana_*. Names, never edits: it touches no finding, no number, no data-* id, no layout — so the Verify layer is untouched and the Programmer renders the masthead + figcaptions from copywriter.json verbatim. Runs at Stage 3.5, after the Editor, before the Designer.
criticReview a finished Data2Story blog against the 5 quality rubric dimensions (visual_design, narrative_pacing, data_method_transparency, claim_data_alignment, insight_value), score each 1-7 with on-page evidence, and emit critic.json with pass/fail + targeted, surgical send-back instructions. Verifies every load-bearing claim/asset against its traceability chain before scoring; applies the caveat-survival, honest-accuracy, and third-party-attribution caps. Does NOT rewrite content — scores and sends back. Use at Stage 6.5 after the Auditor and after verify.py has produced verifier.json; re-runs each revision round. Triggers: a built index.html plus verifier.json exist, or you need to judge whether the article is actually good.
data2storyData Journalist Agent (Data2Story) — orchestrator: turn a dataset into a blog. Runs detective → analyst → editor → designer → programmer → auditor → inspector in sequence. Creates a versioned project folder for each run.
data2story-proUse to turn a dataset into a verifiable multimedia blog (a data story / data-driven article / interactive dashboard from a dataset). Orchestrator for the Data Journalist Agent (Data2Story): a 7-team newsroom (14 agents) running detective → scout → analyst → imagineer → editor → copywriter → designer → interaction → hero → cinematographer → programmer → auditor → critic → inspector in sequence. Trigger when the user hands over a dataset (CSV/JSON/folder/path) and wants a published story, blog post, or interactive report built from it. Creates a versioned project folder per run.
designerRead editor.md, editor.json, and analyst.json. Make data-driven creative visual decisions for every section — teaser, charts, images, videos, audio, maps, and interactive demos when they fit the data. Generate selected assets. No HTML. Outputs designer.json with des_xx IDs.
detectiveResearch external context for a dataset — domain background, history, related studies, and why this data matters. Outputs detective.json (structured findings with det_xx IDs) before any analysis begins.
editorRead analyst.json and detective.json, make all editorial decisions — what the blog argues, which findings matter, narrative arc and section structure. Runs after the Analyst/Imagineer, before the Designer. No visual design. Outputs editor.md (prose) and editor.json (structure with edt_xx IDs).