CO2.js Footer: Working Demo
This page demonstrates the sustainability footer pattern used on SUSTAINABILITY.md. Scroll to the bottom to see it in action. The values update once all resources have loaded.
The pattern satisfies the W3C Web Sustainability Guidelines call for WSG 5.x — Establish Sustainability Reporting Mechanisms by surfacing a live per-page-view carbon estimate to every visitor without adding a server-side dependency or a heavy third-party bundle.
1. What the footer shows
| Metric | Source | Notes |
|---|---|---|
| Page emissions (gCO₂e / page view) | CO2.js SWD v4 model | Directional estimate; not a precise measurement |
| Transfer size | Performance Navigation Timing API | Sum of all navigation and resource entries |
| Green hosting | Green Web Foundation hosting check API | Checks the current page hostname; result may vary by environment |
2. Copy-paste HTML
Add the following block to your page template. The three
<span> elements are updated by the script in
Section 3.
<p class="footer-metrics">
<strong>Page emissions:</strong>
<span id="footer-co2-estimate">Calculating…</span>
·
<strong>Transfer:</strong>
<span id="footer-co2-bytes">Calculating…</span>
·
<strong>Green hosting:</strong>
<span id="footer-green-hosting">Checking…</span>
·
<strong>Method:</strong>
<a href="https://www.thegreenwebfoundation.org/news/start-calculating-digital-carbon-emissions-in-5-minutes-with-co2-js/"
>CO2.js quickstart</a>
</p>
3. Copy-paste script
Paste this <script type="module"> block before
the closing </body> tag. No build step or
npm install is required — CO2.js loads from a CDN.
For production use, replace @latest with a pinned
version (e.g. @0.18.0) so upstream releases do not
silently change your reported numbers.
<script type="module">
import { co2 } from "https://cdn.jsdelivr.net/npm/@tgwf/co2@latest/dist/esm/index.js";
import { check as greenHostingCheck } from "https://cdn.jsdelivr.net/npm/@tgwf/co2@latest/dist/esm/hosting.js";
const estimateEl = document.getElementById("footer-co2-estimate");
const bytesEl = document.getElementById("footer-co2-bytes");
const hostingEl = document.getElementById("footer-green-hosting");
const formatBytes = (bytes) => {
if (!Number.isFinite(bytes) || bytes <= 0) return "0 B";
const units = ["B", "KB", "MB", "GB"];
const exponent = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);
const value = bytes / (1024 ** exponent);
return `${value.toFixed(value >= 10 || exponent === 0 ? 0 : 1)} ${units[exponent]}`;
};
const totalTransferBytes = () => {
const entries = [
...performance.getEntriesByType("navigation"),
...performance.getEntriesByType("resource"),
];
return entries.reduce((total, entry) => {
const transfer = Number(entry.transferSize) || 0;
const encoded = Number(entry.encodedBodySize) || 0;
return total + (transfer > 0 ? transfer : encoded);
}, 0);
};
const render = async () => {
if (!estimateEl || !bytesEl || !hostingEl) return;
// ── Emissions estimate ──────────────────────────────────────────────
try {
const bytes = totalTransferBytes();
const calculator = new co2({ model: "swd", version: 4 });
const grams = calculator.perVisit(bytes, false);
bytesEl.textContent = formatBytes(bytes);
estimateEl.textContent = Number.isFinite(grams)
? `${grams.toFixed(3)} gCO2e / page view`
: "Unavailable";
} catch {
bytesEl.textContent = "Unavailable";
estimateEl.textContent = "Unavailable";
}
// ── Green-hosting check ─────────────────────────────────────────────
// Pass the hostname of the page being checked.
// For a static site you can determine this once and hard-code it.
try {
const isGreen = await greenHostingCheck(window.location.hostname);
hostingEl.textContent = isGreen ? "Yes (Green Web Foundation)" : "No / unknown";
} catch {
hostingEl.textContent = "Unavailable";
}
};
// Run after all resources have loaded so transfer sizes are complete.
if (document.readyState === "complete") {
render();
} else {
window.addEventListener("load", render, { once: true });
}
</script>
4. Design trade-offs
| Approach | When to use | Trade-offs |
|---|---|---|
| Static string (e.g. "≈ 0.08 gCO₂e") | Most sites — update after each Lighthouse audit | Zero JS runtime cost; does not vary per load |
| Dynamic (this demo) | Dashboards or pages with variable payloads | Adds CDN JS dependency (~11 KB gzipped); result varies each load; requires a network call for hosting check |
For a static site, the static approach is lower-impact and equally informative. Reserve the dynamic pattern for cases where the per-load number is meaningfully different.
5. WSG references
| Guideline | How this demo addresses it |
|---|---|
| WSG 5.x — Establish Sustainability Reporting Mechanisms | Per-page-view CO₂ estimate visible to every visitor in the footer |
| WSG 5.28 — Include Sustainability Information in Reports | Transfer size and hosting source disclosed alongside emissions estimate |