# Faostat Infographic

> Use when the user wants a modern, non-expert-facing visual summary of FAOSTAT data on a single page — an infographic, one-pager, visual summary, explainer card, or shareable graphic for social, pitch decks, or press use. The deliverable is a standalone HTML file with inline SVG (optional PNG/PDF export). Aesthetic — Visual Capitalist / Our World in Data explainer cards / Statista — bold typograph…

- **Type:** Skill
- **Install:** `agentstack add skill-berba-q-faostat-skills-infographic`
- **Verified:** Pending review
- **Seller:** [berba-q](https://agentstack.voostack.com/s/berba-q)
- **Installs:** 0
- **Category:** [AI & ML](https://agentstack.voostack.com/c/ai-and-ml)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [berba-q](https://github.com/berba-q)
- **Source:** https://github.com/berba-q/faostat-skills/tree/main/skills/infographic
- **Website:** https://github.com/berba-q/faostat-skills

## Install

```sh
agentstack add skill-berba-q-faostat-skills-infographic
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# FAOSTAT Infographic

Build a single-page infographic from FAOSTAT data for a non-expert audience. The reader should grasp the main point in under 10 seconds and the supporting detail in under 30. Aesthetic cue: modern data journalism (Visual Capitalist, OWID explainer cards, Statista) — not FAO yearbooks.

## Prerequisites

Before starting, confirm the FAOSTAT MCP tools are available: `faostat_get_data`, `faostat_search_codes`, `faostat_list_groups`, `faostat_list_domains`, `faostat_get_rankings`. If not, stop and tell the user this skill requires the FAOSTAT MCP server.

## Invariants

Cross-skill invariants (all six — violations are skill bugs):

1. **FILTER vs DISPLAY codes.** `faostat_get_data` takes FILTER codes (e.g., `2510` for Production). `faostat_get_rankings` takes DISPLAY codes (e.g., `5510`). Never invert.
2. **Year syntax.** Comma-separated lists only (`'2010,2011,...,2023'`); colon ranges return empty in practice.
3. **Element filter required** on every `faostat_get_data` call.
4. **TCL for national trade aggregates, TM only for partner breakdowns.** Never sum TM rows to reconstruct national totals.
5. **China composite default (Apr 2026 user preference).** For any country-level number or ranking, default to composite `China` (area 351). Offer `China, mainland` (41) as an opt-in. Flag the choice in the source footer. Map carve-out: if the main visual is a choropleth, the map uses the disaggregation path (area 41 on the CHN polygon; HKG 96 / MAC 128 / TWN 214 on their own polygons). Never blend the two in the same figure.
6. **`faostat_get_rankings` HTTP-500 fallback.** If the call fails, reconstruct by pulling `faostat_get_data` across all reporting countries and sorting client-side. Note the fallback in the source footer.
7. **Element and item code resolution.** Never use a hardcoded numeric element or item code as the primary value in a `faostat_get_data` call. Always resolve at runtime: `faostat_search_codes(domain_code='', dimension_id='element', query='')` for elements; `faostat_search_codes(domain_code='', dimension_id='item', query='')` for items. Numeric codes shown in reference tables and code examples are verified hints — use them to validate the search result, not as the authoritative source. Domain letter-codes (QCL, TCL, GT, EM, FBS, FS…) are stable and may be used directly.

Infographic-specific invariants:

8. **One hero stat.** There is exactly one hero number on the page — the most surprising single figure. If you're torn between two, push the second into the supporting-stats row.
9. **One main visual per layout mode.** Two modes:
   - **Poster mode (default):** one chart/map/flow diagram total. A second visual becomes a plain bulleted list or small numeric table — no bar-width indicators, no sparklines.
   - **Narrative mode (opt-in):** the topic warrants multiple sections — e.g., a trade infographic with (1) global volume hero → (2) top exporters bar chart → (3) trade route flow map. Each section has its own sub-headline and one visual element. Activate when the user's topic is inherently multi-angle OR when they explicitly ask. In narrative mode the hero still appears once at the top; each section's visual is smaller than the hero section. Maximum 3 sections before the takeaway.
10. **Jargon only in the source footer.** `AR5`, `AR5 GWP-100`, `CAGR`, `n.e.c.`, `FILTER code`, `DISPLAY code`, `LULUCF`, bare `CO2eq`, `kt`/`Mt`/`Gt` on first reference, and numeric FAOSTAT element/item codes stay in the source footer. Not in visual titles, subtitles, chart labels, captions, or headlines.
11. **Ten-second test.** Read the page aloud in 10 seconds — can a non-expert recite the main point? If not, shrink the headline or enlarge the hero.
12. **No FAO branding.** Retain CC-BY-4.0 data attribution ("Data: FAOSTAT (FAO), CC-BY-4.0"), but do not reproduce the FAO logo, "Food and Agriculture Organization of the United Nations" masthead, ISSN, "FAO Statistics Division" stamp, or "Required citation: FAO. …" line. The infographic is the analyst's, not FAO's.

## Visual system

### Palettes (skill auto-picks by topic; user can override)

| palette | background | hero | accent | text | secondary | fits |
|---|---|---|---|---|---|---|
| **Ember** | `#0B0B0F` | `#F6A33F` | `#FF5C39` | `#F3F3F1` | `#6D6D74` | climate, emissions, trade, shocks |
| **Meadow** | `#F6F5EF` | `#2E7D4F` | `#E8A03D` | `#1F2420` | `#6B6E67` | production, food security, nutrition |
| **Ink** | `#FAFAFA` | `#1B1F3A` | `#E23E57` | `#1B1F3A` | `#7A7D87` | prices, indicators, economic angles |

Topic routing: emissions / temperature / trade disruption → Ember. Crops / livestock / food security / undernourishment → Meadow. Producer prices / CPI / indicators → Ink. If ambiguous, ask the user once.

### Typography (fixed)

- Hero stat: **Space Grotesk 700** (or Inter 900 fallback), 120–180 px on desktop, 72–96 px on mobile.
- Headline: Space Grotesk 700, 40–56 px desktop / 28–36 mobile, ≤ 10 words, plain English.
- Supporting stats: Space Grotesk 700 for the number, Inter 500 for the caption.
- Body / takeaway: Inter 400 italic for the takeaway, Inter 400 for everything else.
- Numbers: IBM Plex Mono 500 for small inline stats inside chart labels (helps numeric alignment).

One font family loaded via Google Fonts (`https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;700&family=Inter:wght@400;500;700;900&family=IBM+Plex+Mono:wght@500&display=swap`).

### Hierarchy (fixed, in page order)

1. **Hero stat** — one number, as big as it dares (≥ 30 % of viewport height on desktop). Unit spelled out ("16.5 billion tonnes of CO₂-equivalent", not "16.5 Gt CO₂eq").
2. **Headline** — one sentence, ≤ 10 words.
3. **Supporting stats row** — 2–4 numbers with a Lucide icon each and a one-line caption. No more than 4.
4. **Main visual** — exactly one chart, map, or flow diagram per section. Default layout has one section. Narrative mode (multi-section) adds intermediate sections — see invariant 9.
5. **Takeaway** — one italic sentence, plain-English "so-what" framing.
6. **Source footer** — "Data: FAOSTAT (FAO), accessed [Month YYYY]. Licence: CC-BY-4.0. Domains: [codes]. China: [composite 351 / mainland 41 per user opt-in / disaggregated for map]."

Everything on one scrollable HTML document, mobile-responsive. The page can be as tall as the story requires — "one page" means no pagination, not one viewport. Max content width 720 px; hero and main visual full-bleed to 1200 px.

### Iconography

Inline **Lucide** SVG icons (MIT licensed, ≤ 1 kB each). Fetch from `https://cdn.jsdelivr.net/npm/lucide-static@latest/icons/.svg` at build time and paste the SVG inline. Icon colour = palette accent. Icon size: 32 px next to supporting stats, 48 px next to the main visual title.

**Two-tier icon selection rule:** choose by *stat type first* (what kind of number is this?), then by *domain* (what is it about?) when the stat type doesn't resolve it.

Stat-type icons (use these before anything domain-specific):

| Stat type | Icon | Notes |
|---|---|---|
| Monetary value (USD, EUR, etc.) | `banknote` | Any dollar/price figure. Do NOT use `ship` for export *value*. |
| Growth rate / percentage change | `trending-up` or `trending-down` | Sign-aware: use `trending-down` for negative. |
| Share / concentration / % of total | `pie-chart` | "59% shipped by top 5" → `pie-chart`, not `ship`. |
| Record / all-time peak | `flame` | "2024 was the peak year" → `flame`, not `calendar`. |
| Ranking / #1 / leader | `trophy` | "Russia is the top exporter" → `trophy`. |
| Milestone / threshold crossed | `zap` | Sudden change, tipping point. |
| Count / number of entities | `hash` | "10 countries account for…" |
| Year / time reference (non-record) | `calendar` | Only when the year itself is the fact, not the record it holds. |
| Physical quantity shipped/moved | `package` | Export *volume* (tonnes, litres). NOT `ship` — `ship` is the vessel. |

Domain icons (use when the stat type is already resolved by the Lucide icon above, or when you need a second icon):

| Domain | Icon |
|---|---|
| Agrifood emissions / GHG | `cloud` |
| Crop production | `wheat` |
| Trade route / logistics | `ship` (only for the concept of shipping, e.g. a section header) |
| Temperature / warming | `thermometer-sun` |
| Producer price / CPI | `coins` |
| Yield / efficiency | `sprout` |
| Livestock | `beef` |
| Water | `droplet` |
| Land | `mountain` |
| Food security / hunger | `utensils` |
| Forest / land cover | `trees` |

### Design principles

These principles are extracted from best-in-class data journalism infographics. Apply them every time:

1. **Narrative arc.** Every infographic tells a complete arc: *scale the problem → show the data → land the implication*. Plan the arc in Step 2 before pulling data. If the data doesn't support the arc, reframe — don't just display numbers.
2. **Progressive disclosure.** A reader stopping after 5 s gets the hero. One stopping after 15 s gets the supporting stats. One reading fully gets the chart and takeaway. Each layer adds detail without requiring the previous layer to be re-read.
3. **Data-ink ratio.** Remove every visual element that doesn't carry a data signal. No decorative borders, no 3-D effects, no unnecessary tick marks, no legend if labels on the data suffice.
4. **Icon as cognitive anchor.** Icons beside supporting stats aren't decoration — they help readers recall the number later. Every supporting stat gets exactly one Lucide icon; the icon carries semantic meaning (not generic icons like `star` or `check`).
5. **Whitespace as structure.** Margins and padding do the job of dividers. Don't add horizontal rules or coloured bands — generous padding between sections is cleaner.
6. **Typography does the heavy lifting.** The hero number should be readable from arm's length. If a reader needs to lean in to read the hero, it's too small.

### Animations (HTML only)

Scroll-triggered, zero external dependencies — `IntersectionObserver` + CSS transitions + inline JS. Every animation must respect `prefers-reduced-motion: reduce` via a single early-return guard.

**Hero counter** — the hero number counts from 0 to its final value over 1.2 s (ease-out cubic). Store the numeric portion in `data-value` on `.hero-stat`; keep unit and prefix in separate `` elements so only the digit string animates.

**Supporting-stats cascade** — each `.stat` card starts invisible (`opacity: 0; transform: translateY(16px)`) and transitions in with a 100 ms stagger as the section enters the viewport.

**Bar chart draw** — each bar `` starts at `width="0"` (or `height="0"` for horizontal). Set the target dimension via a CSS custom property; transition to it over 0.8 s with 50 ms per-bar stagger.

**Line chart draw** — set `stroke-dasharray` equal to the path's `getTotalLength()` at paint time; start `stroke-dashoffset` at that same value and transition to `0` over `1.4s cubic-bezier(0.25, 1, 0.5, 1)`. Triggered by an `IntersectionObserver` adding `.visible`.

**Hero glow pulse** (Ember palette only) — a repeating `box-shadow` keyframe on the hero stat container. Fades an amber glow in and out over 2 s. Omit on Meadow and Ink — those topics don't warrant urgency drama.

**Trade route arcs + moving icons** — for flow maps. Three layered animations:
1. Arc draw: `stroke-dashoffset` transitions from full path length to 0, staggered 200 ms per route, 1.2 s each.
2. Icon travel: CSS `offset-path` matching the arc's Bézier definition; `offset-rotate: auto` keeps the icon facing the direction of travel.
3. Pulse dot at destination: a small circle at the importer centroid scales from 0 → 1.4 → 1 (`transform: scale`) when the icon arrives, timed via `animation-delay`.

```css
/* Arc draw-on */
.trade-arc {
  stroke-dasharray: var(--arc-len);   /* set via JS: el.style.setProperty('--arc-len', path.getTotalLength()) */
  stroke-dashoffset: var(--arc-len);
  transition: stroke-dashoffset 1.2s cubic-bezier(.4,0,.2,1);
}
.trade-arc.visible { stroke-dashoffset: 0; }

/* Icon travel — offset-path set inline per route */
.trade-icon {
  offset-rotate: auto;
  animation: _travel var(--dur, 5s) linear var(--delay, 0s) infinite;
}
@keyframes _travel {
  0%   { offset-distance: 0%;   opacity: 0; }
  5%   { opacity: 1; }
  95%  { opacity: 1; }
  100% { offset-distance: 100%; opacity: 0; }
}

/* Destination pulse */
.dest-dot {
  animation: _pulse 0.4s ease-out var(--arrive-delay, 4.8s) both;
}
@keyframes _pulse {
  0%   { transform: scale(0); opacity: 1; }
  70%  { transform: scale(1.4); }
  100% { transform: scale(1); opacity: 0.6; }
}
```

Standard JS boilerplate (inline `` at end of ``):

```javascript
document.addEventListener('DOMContentLoaded', () => {
  if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;

  const io = new IntersectionObserver((entries) => {
    entries.forEach(e => {
      if (!e.isIntersecting) return;
      e.target.classList.add('visible');
      if (e.target.classList.contains('hero-stat')) _counter(e.target);
      if (e.target.dataset.draw) _drawPath(e.target);
      io.unobserve(e.target);
    });
  }, { threshold: 0.15 });

  document.querySelectorAll('.hero-stat, .stat, [data-draw]').forEach(el => io.observe(el));

  function _counter(el) {
    const end = parseFloat(el.dataset.value);
    const decimals = (String(end).split('.')[1] ?? '').length;
    const t0 = performance.now();
    (function tick(now) {
      const p = Math.min((now - t0) / 1200, 1);
      const v = (end * (1 - Math.pow(1 - p, 3))).toFixed(decimals);
      el.querySelector('.counter-value').textContent = v;
      if (p  { el.style.strokeDashoffset = 0; });
  }
});
```

CSS additions inside ``:

```css
@media (prefers-reduced-motion: no-preference) {
  .stat { opacity: 0; transform: translateY(16px);
          transition: opacity .5s ease, transform .5s ease; }
  .stat.visible { opacity: 1; transform: none; }
  .stat:nth-child(2) { transition-delay: .1s; }
  .stat:nth-child(3) { transition-delay: .2s; }
  .stat:nth-child(4) { transition-delay: .3s; }

  /* Ember palette hero pulse */
  body.palette-ember .hero-stat {
    animation: _glow 2s ease-in-out infinite;
  }
  @keyframes _glow {
    0%, 100% { box-shadow: 0 0 0 transparent; }
    50%       { box-shadow: 0 0 52px rgba(246,163,63,.30); }
  }
}
```

Add `class="palette-ember"` (or `palette-meadow` / `palette-ink`) to `` to activate the correct palette-scoped animation rules.

## Workflow

### Step 1 — Gather parameters

Ask the user (via `AskUserQuestion` if Cowork, inline otherwise) for anything not specified:
- Topic / angle
- Time window (endpoint year + optional start year for a change framing)
- Scope: global (default) / a region / a country
- Palette preference (Ember / Meadow / Ink / auto-pick)
- Output format preferences: HTML only (default), + PNG, + PDF, + companion CSV

Proceed without a second clarifying round — one is enough. Pick sensible defaults for anything still unspecified.

### Step 2 — Design the hero message

From the topic, identify the single most striking stat. Rules of thumb:
- **Biggest delta over the window** (e.g., "+127 % in aquaculture output since 2000", "+48.8 % in pre- and post-production emissions since 2001", "wheat yield in the EU grew 3× faster than Sub-Saharan Africa over 30 years")
- **Most extreme ratio** (e.g., "4× gap in cattle-meat emissions intensity between Africa and Europe", "top 5 countries account for 72 % of global wheat production")
- **Most surprising ranking** (e.g., "India overtook the US as the world's top rice exporter in 2021", "top 10 emitters = 55 % of world total")
- **A number at the edge of intuition** (e.g., "815 million people undernourished — roughly 1 in 10", "16.5 billion tonnes of CO₂-equivalent", "Brazil's soybean exports tripled in 20 years")

This becomes the hero + headline. Draft both before pulling data — if the narrative falls apart on the numbers, rewrite.

### Step 3 — Plan supporting stats and visual

2–4 suppo

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [berba-q](https://github.com/berba-q)
- **Source:** [berba-q/faostat-skills](https://github.com/berba-q/faostat-skills)
- **License:** MIT
- **Homepage:** https://github.com/berba-q/faostat-skills

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** yes
- **Shell / process execution:** yes
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: flagged — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-berba-q-faostat-skills-infographic
- Seller: https://agentstack.voostack.com/s/berba-q
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
