Install
$ agentstack add skill-berba-q-faostat-skills-infographic Open-source listing, not yet scanned by AgentStack. Follow the source repository for install instructions.
Security review
⚠ Flagged1 finding(s); flagged for manual review. · v0.1.0 How review works →
- • Prompt-injection patterns
- • Secret / credential exfiltration
- • Dangerous shell & filesystem operations
- • Untrusted network calls
- • Known-malicious package signatures
- high Dangerous shell/eval execution.
What it can access
- ✓ Network access No
- ● Filesystem access Used
- ● Shell / process execution Used
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →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):
- FILTER vs DISPLAY codes.
faostat_get_datatakes FILTER codes (e.g.,2510for Production).faostat_get_rankingstakes DISPLAY codes (e.g.,5510). Never invert. - Year syntax. Comma-separated lists only (
'2010,2011,...,2023'); colon ranges return empty in practice. - Element filter required on every
faostat_get_datacall. - TCL for national trade aggregates, TM only for partner breakdowns. Never sum TM rows to reconstruct national totals.
- China composite default (Apr 2026 user preference). For any country-level number or ranking, default to composite
China(area 351). OfferChina, 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. faostat_get_rankingsHTTP-500 fallback. If the call fails, reconstruct by pullingfaostat_get_dataacross all reporting countries and sorting client-side. Note the fallback in the source footer.- Element and item code resolution. Never use a hardcoded numeric element or item code as the primary value in a
faostat_get_datacall. 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:
- 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.
- 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.
- Jargon only in the source footer.
AR5,AR5 GWP-100,CAGR,n.e.c.,FILTER code,DISPLAY code,LULUCF, bareCO2eq,kt/Mt/Gton first reference, and numeric FAOSTAT element/item codes stay in the source footer. Not in visual titles, subtitles, chart labels, captions, or headlines. - 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.
- 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)
- 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").
- Headline — one sentence, ≤ 10 words.
- Supporting stats row — 2–4 numbers with a Lucide icon each and a one-line caption. No more than 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.
- Takeaway — one italic sentence, plain-English "so-what" framing.
- 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:
- 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.
- 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.
- 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.
- 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
starorcheck). - 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.
- 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:
- Arc draw:
stroke-dashoffsettransitions from full path length to 0, staggered 200 ms per route, 1.2 s each. - Icon travel: CSS
offset-pathmatching the arc's Bézier definition;offset-rotate: autokeeps the icon facing the direction of travel. - Pulse dot at destination: a small circle at the importer centroid scales from 0 → 1.4 → 1 (
transform: scale) when the icon arrives, timed viaanimation-delay.
/* 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 `):
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 ``:
@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
- Source: 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.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.