foerdeliebe-sh / kirby-website-carbon
Kirby plugin that renders a Website Carbon rating badge, rated lazily server-side from measured page weight
Package info
github.com/HENNIROCKS/kirby-website-carbon
Type:kirby-plugin
pkg:composer/foerdeliebe-sh/kirby-website-carbon
Requires
- php: ^8.2
- getkirby/composer-installer: ^1.2
Requires (Dev)
- getkirby/cms: ^5.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
A Kirby CMS plugin that renders a self-contained Website Carbon rating badge for the current page.
How it works
The rating depends on how many bytes a page actually transfers — something
only a real browser can tell you. But api.websitecarbon.com blocks requests
from cloud CI IP ranges (Cloudflare JS challenge, reproducible from GitHub
Actions), while accepting them from regular hosting. So the two halves run in
different places:
- Page weight measurement —
scripts/measure.mjsruns in CI via Playwright, on a schedule. It walks yoursitemap.xml, measures the real transferred bytes per page and checks green hosting. No call to the rating API. The result is written tocontent/website-carbon.json. - Rating lookup —
classes/WebsiteCarbon.phpruns live, server-side, on the actual page request. Results are cached (30 days by default), keyed by byte count, so a page is only re-rated when its measured weight changes.
If no measurement exists for a page, the snippet renders nothing at all. That is also what you see before the first CI run.
Requirements
- Kirby 5
- PHP 8.2+
- Node 20+ in CI (for the measurement script)
Installation
Composer
composer require foerdeliebe-sh/kirby-website-carbon
Download
Download the repository and drop it into site/plugins/kirby-website-carbon.
Usage
The snippet
<?php snippet('website-carbon') ?>
Renders nothing if the current page has no measurement yet, so it is safe to place unconditionally — in a footer, for example. It can be used more than once per page; the stylesheet is only emitted on the first use.
The page method
For a custom badge, use the page method directly:
<?php if ($carbon = $page->websiteCarbon()): ?> <p> This page is rated <?= esc($carbon['rating']) ?> and cleaner than <?= esc($carbon['cleanerThan'] * 100) ?>% of pages tested, at <?= esc($carbon['gco2e']) ?> g CO₂ per view. </p> <?php endif ?>
It returns null when there is no measurement or the API is unreachable, and
otherwise an array:
| Key | Example | Description |
|---|---|---|
rating |
"A+" |
Rating from A+ to F |
cleanerThan |
0.94 |
Share of tested pages this one beats (0–1) |
gco2e |
0.06 |
Grams of CO₂ equivalent per page view |
CI setup
Copy examples/website-carbon.yml into the
consuming site as .github/workflows/website-carbon.yml and set SITE_URL
plus the FTP secrets. The measurement itself is a single command:
node site/plugins/kirby-website-carbon/scripts/measure.mjs https://example.com content/website-carbon.json
Playwright is not vendored with the plugin; install it in the job with
npm install --no-save playwright && npx playwright install --with-deps chromium.
The resulting JSON then has to reach the live site's content/ folder. The
example workflow uploads that one file over FTPS with curl. Do not use a
folder-syncing deploy action for this: those compare the local folder against
a remote state file and delete whatever is missing locally, which can wipe
content edited in the Panel.
If your site's content lives in Git and is deployed from there anyway, the simpler alternative is to commit the file back to the repository and let your regular deploy pick it up.
Options
Set in site/config/config.php:
return [ 'foerdeliebe-sh.kirby-website-carbon.cache.ttl' => 60 * 24 * 7, ];
| Option | Default | Description |
|---|---|---|
data.file |
content/website-carbon.json |
Path to the measurement file. Relative paths resolve from the site's index root. |
cache.ttl |
43200 (30 days) |
Minutes a fetched rating stays cached |
cache.failureTtl |
60 (1 hour) |
Minutes a failed lookup stays cached, so an unreachable API can't block every render |
styles |
true |
Whether the snippet loads the plugin's own stylesheet |
link.title |
null |
title attribute of the badge link; null uses the translation (see below) |
A note on the link title
Kirby hardcodes the translation locale to en on single-language sites
(AppTranslations.php), so the shipped German translation only applies to
multi-language installations. On a single-language site in another language,
set the title explicitly:
'foerdeliebe-sh.kirby-website-carbon.link.title' => 'Mehr über Website Carbon erfahren',
Or pass it per call: snippet('website-carbon', ['title' => '…']).
Styling
The snippet loads its own stylesheet (once per request) via a <link> tag
pointing at the plugin's assets/website-carbon.css, so the badge looks right
regardless of your CSS build pipeline. Classes: .website-carbon,
.website-carbon__icon, .website-carbon__badge.
The badge inherits color and font-size from its surroundings. Everything
else can be overridden with CSS custom properties from your own stylesheet:
| Property | Default |
|---|---|
--website-carbon-gap |
0.4em |
--website-carbon-badge-background |
rgba(127, 127, 127, 0.15) |
--website-carbon-badge-radius |
0.2em |
--website-carbon-badge-color |
inherit |
To style the badge entirely yourself, set the styles option to false.
Troubleshooting
The badge doesn't show up. The snippet renders nothing when there is no
measurement for the current page. Check that content/website-carbon.json
exists on the server and contains a key for this page's URL. URLs are compared
ignoring scheme, www., host case and trailing slash — anything beyond that
(a different domain, a language prefix that isn't in the sitemap) will not
match.
The rating is stale. Ratings are cached for 30 days, but keyed by measured
byte count — a page that changed weight is re-rated on the next request. To
force a refresh, delete site/cache/foerdeliebe-sh.kirby-website-carbon.
License
MIT © Hendrik Berends
