Climate
Social card
A graphic that states one fact, at a fixed aspect ratio (1:1, 4:5, 16:9 or 9:16). It has a kicker, a headline, one exhibit, a source and a credit, painted on the theme surface. A button downloads the whole card as a PNG.
- Use when
- Use it for one finding that will be shared in a feed, a slide or a chat thread with no text around it. State the fact with its number in the headline. Use one simple exhibit, such as one highlighted bar, one line or one number. Put the source and your name on the card, because it will be shared without the page. Use 1:1 or 4:5 for feeds, 16:9 for slides and link previews, 9:16 for stories.
- Avoid when
- More than one fact or more than one exhibit (use story deck for a sequence, executive brief for a page). Charts that need a legend, small labels or hovering to read, because a shared image is not interactive. An exhibit with its own
title, because the card's headline is the title.
Preview
A template is a whole page, so the preview scrolls inside its frame. Expand it to read it in place, or open it full screen.
import { SocialCard, registerAll } from "insightcharts";
// The slot props below are JSON specs. This makes their `type` names resolvable.
registerAll();
export function Example() {
return (
<SocialCard
aspect="1:1"
kicker="Climate"
title="The world was 1.6°C warmer in 2024 than in 1850 to 1900"
exhibit={{
type: "bar-chart",
data: [
{ year: "2015", c: 1.17 },
{ year: "2016", c: 1.31 },
{ year: "2017", c: 1.22 },
{ year: "2018", c: 1.14 },
{ year: "2019", c: 1.26 },
{ year: "2020", c: 1.31 },
{ year: "2021", c: 1.14 },
{ year: "2022", c: 1.18 },
{ year: "2023", c: 1.48 },
{ year: "2024", c: 1.6 },
],
x: "year",
y: "c",
orientation: "vertical",
sort: "none",
highlight: "2024",
format: "{value}°C",
labels: false,
subtitle: "Global average temperature above the 1850 to 1900 level",
annotations: [
{ type: "line", axis: "y", value: 1.5, label: "Paris Agreement limit" },
],
}}
source="Copernicus Climate Change Service, ERA5 (rounded)"
note="Figures are approximate, for illustration"
credit="InsightCharts"
/>
);
}{
"type": "social-card",
"aspect": "1:1",
"kicker": "Climate",
"title": "The world was 1.6°C warmer in 2024 than in 1850 to 1900",
"exhibit": {
"type": "bar-chart",
"data": [
{ "year": "2015", "c": 1.17 },
{ "year": "2016", "c": 1.31 },
{ "year": "2017", "c": 1.22 },
{ "year": "2018", "c": 1.14 },
{ "year": "2019", "c": 1.26 },
{ "year": "2020", "c": 1.31 },
{ "year": "2021", "c": 1.14 },
{ "year": "2022", "c": 1.18 },
{ "year": "2023", "c": 1.48 },
{ "year": "2024", "c": 1.6 }
],
"x": "year",
"y": "c",
"orientation": "vertical",
"sort": "none",
"highlight": "2024",
"format": "{value}°C",
"labels": false,
"subtitle": "Global average temperature above the 1850 to 1900 level",
"annotations": [
{ "type": "line", "axis": "y", "value": 1.5, "label": "Paris Agreement limit" }
]
},
"source": "Copernicus Climate Change Service, ERA5 (rounded)",
"note": "Figures are approximate, for illustration",
"credit": "InsightCharts"
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Composition
Built from
The social card template is assembled from 4 atoms. Any slot also takes any other component.
Atoms
4Compares a value across categories with bars from a zero baseline.
A featured statistic with its label, change and the context that gives it meaning.
A sourcing footer for a whole piece: sources with optional links, the period covered, method, caveats and credit.
Shows shares of a whole as counted cells in a grid, such as 37 in every 100.
Installation
$ pnpm dlx shadcn@latest add @insightcharts/social-cardThe CLI copies the source into your project and needs the @insightcharts registry in components.json; see installation. With the package, import it by name:
import { SocialCard } from "insightcharts";Examples
16:9, public health, inverse tone
import { SocialCard, registerAll } from "insightcharts";
// The slot props below are JSON specs. This makes their `type` names resolvable.
registerAll();
export function Example() {
return (
<SocialCard
aspect="16:9"
tone="inverse"
kicker="Public health"
title="Reported measles cases in Europe rose from 61,000 in 2023 to 127,000 in 2024"
exhibit={{
type: "bar-chart",
data: [
{ year: "2019", cases: 106 },
{ year: "2020", cases: 12 },
{ year: "2021", cases: 0.2 },
{ year: "2022", cases: 0.9 },
{ year: "2023", cases: 61 },
{ year: "2024", cases: 127 },
],
x: "year",
y: "cases",
orientation: "vertical",
sort: "none",
highlight: "2024",
format: "{value}k",
subtitle: "Reported measles cases, WHO European Region, thousands",
}}
source="WHO and UNICEF (rounded)"
note="Figures are approximate, for illustration"
credit="Data desk"
/>
);
}{
"type": "social-card",
"aspect": "16:9",
"tone": "inverse",
"kicker": "Public health",
"title": "Reported measles cases in Europe rose from 61,000 in 2023 to 127,000 in 2024",
"exhibit": {
"type": "bar-chart",
"data": [
{ "year": "2019", "cases": 106 },
{ "year": "2020", "cases": 12 },
{ "year": "2021", "cases": 0.2 },
{ "year": "2022", "cases": 0.9 },
{ "year": "2023", "cases": 61 },
{ "year": "2024", "cases": 127 }
],
"x": "year",
"y": "cases",
"orientation": "vertical",
"sort": "none",
"highlight": "2024",
"format": "{value}k",
"subtitle": "Reported measles cases, WHO European Region, thousands"
},
"source": "WHO and UNICEF (rounded)",
"note": "Figures are approximate, for illustration",
"credit": "Data desk"
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
9:16, transport, a proportion instead of a chart
import { SocialCard, registerAll } from "insightcharts";
// The slot props below are JSON specs. This makes their `type` names resolvable.
registerAll();
export function Example() {
return (
<SocialCard
aspect="9:16"
kicker="Transport"
title="58% of car journeys in England are under five miles"
deck="Each square is 1% of car journeys."
exhibit={{
type: "waffle-chart",
data: [
{ group: "Under five miles", share: 58 },
],
name: "group",
value: "share",
total: 100,
restLabel: "Five miles or more",
legend: false,
aspect: 1,
}}
source="National Travel Survey"
note="Rounded, approximate figures for illustration"
credit="@datadesk"
/>
);
}{
"type": "social-card",
"aspect": "9:16",
"kicker": "Transport",
"title": "58% of car journeys in England are under five miles",
"deck": "Each square is 1% of car journeys.",
"exhibit": {
"type": "waffle-chart",
"data": [
{ "group": "Under five miles", "share": 58 }
],
"name": "group",
"value": "share",
"total": 100,
"restLabel": "Five miles or more",
"legend": false,
"aspect": 1
},
"source": "National Travel Survey",
"note": "Rounded, approximate figures for illustration",
"credit": "@datadesk"
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
4:5, personal finance, a single number
import { SocialCard, registerAll } from "insightcharts";
// The slot props below are JSON specs. This makes their `type` names resolvable.
registerAll();
export function Example() {
return (
<SocialCard
aspect="4:5"
kicker="Money"
title="Starting to save at 35 instead of 25 leaves $296,000 less at 65"
exhibit={{
type: "big-number",
size: "hero",
value: "$296,000",
label: "Less at 65 if you start at 35 instead of 25",
context: "Saving $300 a month at a 6% real return",
}}
source="Author's calculation"
note="Illustrative"
credit="InsightCharts"
downloadable={false}
/>
);
}{
"type": "social-card",
"aspect": "4:5",
"kicker": "Money",
"title": "Starting to save at 35 instead of 25 leaves $296,000 less at 65",
"exhibit": {
"type": "big-number",
"size": "hero",
"value": "$296,000",
"label": "Less at 65 if you start at 35 instead of 25",
"context": "Saving $300 a month at a 6% real return"
},
"source": "Author's calculation",
"note": "Illustrative",
"credit": "InsightCharts",
"downloadable": false
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| titlerequired | string | The card's one fact, as a plain sentence with the number in it. | |
| kicker | string | Small label above the title. | |
| deck | string | One short line under the title: the measure, or the number in words. | |
| exhibit | Slot | The one exhibit. A chart spec is given the height left in the card (any height you set wins); leave out its title. | |
| source | string | Where the data comes from, shown as "Source: ...". | |
| note | string | Caveat shown after the source, e.g. "Illustrative data". | |
| credit | string | Who made it: a name, handle or publication. | |
| aspect | "1:1" | "4:5" | "16:9" | "9:16" | "1:1" | Shape of the card. |
| tone | "surface" | "inverse" | "surface" | inverse paints the card in the opposite colour mode. |
| downloadable | boolean | true | Show the download button under the card. The image is the whole card, drawn from the DOM onto a canvas. |
| exportWidth | number | 1080 | Width of the downloaded PNG in pixels (1920 for 16:9). |
| filename | string | File name without extension. Defaults to a slug of the title. | |
| downloadLabel | string | "Download PNG" | Label of the download button. |
| maxWidth | number | Widest the card is drawn on screen, in px. Default 540 (520 for 4:5, 720 for 16:9, 400 for 9:16). In narrower containers the whole card is scaled down, like an image, so its composition never changes. | |
| onExport | (blob: Blob) => void | Called with the PNG after a download. | |
| level | 1 | 2 | 3 | 4 | 5 | 6 | 2 | Heading level of the title. |
| theme | ThemeInput | Theme override, passed on to the exhibit. | |
| mode | "auto" | "system" | "light" | "dark" | "auto" | Colour mode. |