diff --git a/public/_redirects b/public/_redirects
new file mode 100644
index 0000000..718072d
--- /dev/null
+++ b/public/_redirects
@@ -0,0 +1,2 @@
+/carbon.txt / 404
+/.well-known/carbon.txt / 404
diff --git a/src/_data/caseStudies.js b/src/_data/caseStudies.js
index 7558d21..bdc993e 100644
--- a/src/_data/caseStudies.js
+++ b/src/_data/caseStudies.js
@@ -1,29 +1,51 @@
-const dev = process.env.NODE_ENV !== 'production'
+const dev = process.env.NODE_ENV !== "production";
const caseStudies = async () => {
- const { devData } = await import('../helpers/dev/caseStudies.js');
+ const { devData } = await import("../helpers/dev/caseStudies.js");
- // if (dev) {
- // return devData;
- // }
+ // if (dev) {
+ // return devData;
+ // }
- const co2js = await fetch('https://www.thegreenwebfoundation.org/wp-json/wp/v2/posts?per_page=100&categories=43,34').then(resp => resp.json()).then(data => data.filter(item => item.categories.includes(43) && item.categories.includes(34)))
+ const co2js = await fetch(
+ "https://www.thegreenwebfoundation.org/wp-json/wp/v2/posts?per_page=100&categories=43,34",
+ )
+ .then((resp) => resp.json())
+ .then((data) =>
+ data.filter(
+ (item) => item.categories.includes(43) && item.categories.includes(34),
+ ),
+ );
- // This is temporary until we've got more case studies. Since they won't all live on our website, we need to figure out a way to get them all in one place.
- const gaw = [
- {
- featured_media: "https://browser-worker.fershad.workers.dev/?title=Making+this+website+respond+to+your+local+energy+grid&page=https://fershad.com/writing/making-this-website-grid-aware/",
- title: {
- rendered: "Making this website respond to your local energy grid"
- },
- excerpt: {
- rendered: "Fershad Irani covers the technical steps I've taken to make this website respond to a visitor's energy grid. It also touches on the design changes that happen as a result, and the performance and energy impacts of those changes."
- },
- link: "https://fershad.com/writing/making-this-website-grid-aware/"
- }
- ]
+ // This is temporary until we've got more case studies. Since they won't all live on our website, we need to figure out a way to get them all in one place.
+ const gaw = [
+ {
+ featured_media:
+ "https://branch.climateaction.tech/wp-content/uploads/2025/07/Branch-medium-intensity-mode-2048x1206.jpg",
+ title: {
+ rendered: "Designing a Grid-Aware Branch",
+ },
+ excerpt: {
+ rendered:
+ "Issue 9 of Branch arrives with a fresh site redesign. This new design builds on valuable feedback we’ve received from you, our community, and some brand-new ideas we’ve been exploring as part of Green Web Foundation’s Grid-aware Websites project.",
+ },
+ link: "https://branch.climateaction.tech/issues/issue-9/designing-a-grid-aware-branch/",
+ },
+ {
+ featured_media:
+ "https://browser-worker.fershad.workers.dev/?title=Making+this+website+respond+to+your+local+energy+grid&page=https://fershad.com/writing/making-this-website-grid-aware/",
+ title: {
+ rendered: "Making this website respond to your local energy grid",
+ },
+ excerpt: {
+ rendered:
+ "Fershad Irani covers the technical steps I've taken to make this website respond to a visitor's energy grid. It also touches on the design changes that happen as a result, and the performance and energy impacts of those changes.",
+ },
+ link: "https://fershad.com/writing/making-this-website-grid-aware/",
+ },
+ ];
- return {co2js, gaw};
-}
+ return { co2js, gaw };
+};
module.exports = caseStudies;
diff --git a/src/docs/carbon-txt/explainer/carbon-txt-validator-architecture.md b/src/docs/carbon-txt/explainer/carbon-txt-validator-architecture.md
index ec2d10d..9cd4cc4 100644
--- a/src/docs/carbon-txt/explainer/carbon-txt-validator-architecture.md
+++ b/src/docs/carbon-txt/explainer/carbon-txt-validator-architecture.md
@@ -29,7 +29,7 @@ The following diagrams use the [C4 model](https://c4model.com/) for describing s

-The context view demonstrate how we expect the the validator to be used by end-users.
+The context view demonstrate how we expect the validator to be used by end-users.
These might be people using the validator via the [carbon.txt website](https://carbontxt.org/tools/validator), as command line tool, or in a data pipeline,
@@ -86,4 +86,4 @@ So, for a CSRD report that is written to fit pre-agreed standards, like being wr
The In our CSRD Processor uses [Arelle](https://arelle.readthedocs.io/), an open source library for working with XBRL documents, to turn XBRL-formatted CSRD reports into datastructures that can be manipulated in Python, and that can be checked for the existence of specific data points.
-The validation results are returned in API respones, or the output in CLI commands.
+The validation results are returned in API responses, or the output in CLI commands.
diff --git a/src/docs/co2js/methods.md b/src/docs/co2js/methods.md
index e20d85f..8b00154 100644
--- a/src/docs/co2js/methods.md
+++ b/src/docs/co2js/methods.md
@@ -134,7 +134,7 @@ The `perVisitTrace()` function accepts the following parameters:
The `options` parameter can contain any of the following keys. These can be used to adjust the values used by the Sustainable Web Design Model's calculation.
-- `dataReloadRatio` Optional – a `number` between 0 and 1 representing the percentage of data that is downloaded by return visitors.
+- `dataReloadRatio` Optional – a `number` between 0 and 1 representing the percentage of data that is downloaded by return visitors. This is the complement of the data cache ratio from the Sustainable Web Design model, i.e. dataReloadRatio = 1 - dataCacheRatio.
- `firstVisitPercentage` Optional – a `number` between 0 and 1 representing the percentage of new visitors.
- `returnVisitPercentage` Optional – a `number` between 0 and 1 representing the percentage of returning visitors.
- `greenHostingFactor`
Sustainable Web Design Model v4 only
- The portion of hosting services powered by renewable or zero-carbon energy, between 0 and 1. If the `green hosting` boolean above is set to `true` then the `greenHostingFactor` will always be `1`.
@@ -173,6 +173,6 @@ The `perVisitTrace()` function returns an object with the following keys:
- `dataCenter` – A `number` representing the carbon intensity for this segment (in grams per kilowatt-hour) used in the calculation.
- `networks` – A `number` representing the carbon intensity for this segment (in grams per kilowatt-hour) used in the calculation.
- `production` - A `number` representing the carbon intensity for this segment (in grams per kilowatt-hour) used in the calculation.
- - `dataReloadRatio` – a `number` between 0 and 1 representing the percentage of data that is downloaded by return visitors.
+ - `dataReloadRatio` – a `number` between 0 and 1 representing the percentage of data that is downloaded by return visitors. This is the complement of the data cache ratio from the Sustainable Web Design model, i.e. dataReloadRatio = 1 - dataCacheRatio.
- `firstVisitPercentage` – a `number` between 0 and 1 representing the percentage of new visitors.
- `returnVisitPercentage` – a `number` between 0 and 1 representing the percentage of returning visitors.
diff --git a/src/docs/grid-aware-websites/plugins/cloudflare-workers.md b/src/docs/grid-aware-websites/plugins/cloudflare-workers.md
index d5a9ded..64a63f8 100644
--- a/src/docs/grid-aware-websites/plugins/cloudflare-workers.md
+++ b/src/docs/grid-aware-websites/plugins/cloudflare-workers.md
@@ -25,7 +25,105 @@ In your Cloudflare Workers project, install this plugin by running the following
npm install @greenweb/gaw-plugin-cloudflare-workers
```
-## Fetching location
+## Quickstart
+
+The easiest way to use this plugin is by utilising the `gridAwareAuto` functionality that it provides. As a minimum, you would need to have the below code in your Cloudflare Worker.
+
+Install this library in your project using `npm install @greenweb/gaw-plugin-cloudflare-workers`.
+
+> [!IMPORTANT]
+> To use this function you also need to have a valid [Electricity Maps API](https://portal.electricitymaps.com/) key with access to the [Carbon Aware Websites API](https://portal.electricitymaps.com/developer-hub/api/reference#latest-carbon-intensity-level). This function currently uses that API as the source of grid intensity data. The Carbon Aware Websites API is currently only available under a paid plan, but we are in conversation with Electricity Maps on ways to make this data available in some kind of free version. You can track the progress and express your interest in this API [in this issue](https://github.com/thegreenwebfoundation/grid-aware-websites/issues/21).
+
+Replace your Cloudflare Worker with the following code.
+
+```js
+import gridAwareAuto from "@greenweb/gaw-plugin-cloudflare-workers";
+
+export default {
+ async fetch(request, env, ctx) {
+ return gridAwareAuto(request, env, ctx);
+ },
+};
+```
+
+This code will:
+
+1. Get the request location.
+2. Fetch the current relative grid intensity using the [Electricity Maps API](https://portal.electricitymaps.com/).
+3. Return the page to the user.
+
+The `gridAwareAuto` function also accepts an options object as the fourth parameter. This allows for some configuration to be made to the implementation. Accepted options values are:
+
+| Option | Type | Default | Possible values | Description |
+| ----------------------- | ------------ | --------------- | --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
+| `contentType` | String[] | `['text/html']` | Example: ['text/html', 'text/css'] | Defines the content types that should be processed |
+| `ignoreRoutes` | String[] | `[]` | Example: ['/wp-admin', '/assets/js'] | A list of routes where grid-aware code should not be applied |
+| `ignoreGawCookie` | String | `'gaw-ignore'` | "gaw-ignore" | A cookie that when present will result in grid-aware code being skipped |
+| `userOptIn` | Boolean | `false` | true, false | Allows developers to specify if users are required to opt-in to the grid-aware website experience |
+| `locationType` | String | `'latlon'` | "latlon", "country" | Type of location data to use |
+| `htmlChanges` | Object | {} | {"low": HTMLRewriter, "moderate": HTMLRewriter, "high": HTMLRewriter} | An object to capture the different HTML changes that are applied at each different grid intesity level |
+| `htmlChanges.low` | HTMLRewriter | null | Custom HTMLRewriter for page modification at low grid intensity level |
+| `htmlChanges.moderate` | HTMLRewriter | null | Custom HTMLRewriter for page modification at moderate grid intensity level |
+| `htmlChanges.high` | HTMLRewriter | null | Custom HTMLRewriter for page modification at high grid intensity level |
+| `defaultView` | String/null | `null` | null, "low", "moderate", "high" | Default view for the grid-aware website experience |
+| `gawDataApiKey` | String | `''` | "xyz123" | API key for the data source |
+| `infoBar` | Object | `{}` | `{target: "", version: "latest", learnMoreLink: "#", popoverText: "", customViews: ""}` | Configuration for the info bar element |
+| `infoBar.target` | String | `''` | Example: "header", "#info-container" | Target element for the info bar |
+| `infoBar.version` | String | `'latest'` | "latest", "1.0.0" | Version of the info bar to use |
+| `infoBar.learnMoreLink` | String | `'#'` | Example: "https://example.com/learn-more" | Link to learn more about the info bar |
+| `infoBar.popoverText` | String | `''` | Example: "This website adapts based on carbon intensity" | Provide a custom string of text to be used in the info bar popover element |
+| `infoBar.customViews` | String | `''` | Example: "custom-low,custom-moderate,custom-high" | Custom views for the grid-aware website experience |
+| `kvCacheData` | Boolean | `false` | true, false | Whether to cache grid data in KV store. Read [setup instructions](#cache-grid-data-in-cloudflare-workers-kv) |
+| `kvCachePage` | Boolean | `false` | true, false | Whether to cache modified pages in KV store. Read [setup instructions](#cache-page-response-in-cloudflare-workers-kv) |
+| `debug` | String | `"none"` | "none", "full", "headers", "logs" | Activates debug mode which outputs logs and returns additional response headers |
+| `dev` | Boolean | `false` | true, false | Whether to enable development mode |
+| `devConfig` | Object | `{}` | `{hostname: "", port: "", protocol: ""}` | Configuration for development mode |
+| `devConfig.hostname` | String | `''` | Example: "localhost" | Hostname for development mode |
+| `devConfig.port` | String | `''` | Example: "8080" | Port for development mode |
+| `devConfig.protocol` | String | `''` | Example: "http" | Protocol for development mode |
+
+The following example will run on all HTML pages, but will skip any routes (URLs) that include the `/company/` or `/profile/` strings. It will use Electricity Maps as the data source, and uses an API key which has been set as an environment secret. IF grid-aware changes need to be applied to the page, a `data-grid-aware=true` attribute will be set on the HTML element.
+
+```js
+import gridAwareAuto from "@greenweb/gaw-plugin-cloudflare-workers";
+
+export default {
+ async fetch(request, env, ctx) {
+ return gridAwareAuto(request, env, ctx, {
+ // Ignore these routes
+ ignoreRoutes: ["/company/", "/profile/"],
+ // Use this API key that has been saved as a secret
+ gawDataApiKey: env.EMAPS_API_KEY,
+ // Configure the grid-aware info bar
+ infoBar: {
+ target: "#gaw-info-bar",
+ learnMoreLink:
+ "https://www.thegreenwebfoundation.org/tools/grid-aware-websites/",
+ version: "latest",
+ popoverText:
+ "This website adapts based on your local electricity grid's carbon intensity",
+ },
+ // Require users to opt-in to grid-aware experience
+ userOptIn: false,
+ // Set a default view (null means it will be based on actual grid intensity)
+ defaultView: null,
+ // Make these changes to the web page using HTMLRewriter when the grid intensity is high.
+ // All other states (low, moderate) will return the page as normal - no changes applied.
+ htmlChanges: {
+ high: new HTMLRewriter().on("html", {
+ element(element) {
+ element.setAttribute("data-grid-aware", "true");
+ },
+ }),
+ },
+ });
+ },
+};
+```
+
+## Advanced - Fetching location
+
+If you want to have more control over how grid-awareness is applied to your site, you can use this plugin in conjunction with the core [Grid-aware Websites](https://github.com/thegreenwebfoundation/grid-aware-websites) library.
You can use this plugin to fetch the country of a website visitor. This information can then be passed into the `@greenweb/grid-aware-websites` library to enable it to retrieve data about the user's energy grid and make a determination if grid-aware website changes should be applied.
@@ -38,18 +136,17 @@ import { getLocation } from "@greenweb/gaw-plugin-cloudflare-workers";
export default {
async fetch(request, env, ctx) {
-
// Use the getLocation function to check for the user's country in the request object
const location = getLocation(request);
// If there's an error, process the request as normal
if (location.status === "error") {
- return new Response('There was an error');
+ return new Response("There was an error");
}
- // Otherwise we can get the "country" variable
+ // Otherwise we can get the "country" variable
const { country } = location;
- return new Response(`The country is ${country}.`)
+ return new Response(`The country is ${country}.`);
},
};
```
@@ -78,9 +175,9 @@ export default {
return new Response('There was an error');
}
- // Otherwise we can get the "country" variable
+ // Otherwise we can get the "country" variable
const { country } = location;
-
+
// Use the Grid-aware Websites library to fetch data for Taiwan, and check if grid-aware website changes should be applied.
const gridData = await powerBreakdown.check(country);
@@ -126,7 +223,7 @@ const powerBreakdown = new PowerBreakdown({
export default {
async fetch(request, env, ctx) {
-
+
// Use the Grid-aware Websites library to fetch data for Taiwan, and check if grid-aware website changes should be applied.
const gridData = await powerBreakdown.check(country);
@@ -147,7 +244,7 @@ This will save the `gridData` response in the `GAW_DATA_KV` using the `country`
```diff
+ const options = {
-+ expirationTtl: 60 * 60 * 6 // Store the data for 6 hours
++ expirationTtl: 60 * 60 * 6 // Store the data for 6 hours
+ }
- await saveDataToKv(env, country, JSON.stringify(gridData))
@@ -178,7 +275,7 @@ export default {
if (storeData) {
return new Response(storedData)
}
-
+
// Use the Grid-aware Websites library to fetch data for Taiwan, and check if grid-aware website changes should be applied.
const gridData = await powerBreakdown.check(country)
@@ -220,7 +317,6 @@ import { savePageToKv } from "@greenweb/gaw-plugin-cloudflare-workers";
export default {
async fetch(request, env, ctx) {
-
const modifiedPage = `
@@ -230,12 +326,12 @@ export default {
This page has changed.
- `
+ `;
- await saveDataToKv(env, request.url, modifiedPage)
+ await saveDataToKv(env, request.url, modifiedPage);
// If we've got data back using the Grid-aware Websites library, let's return that to the browser
- return new Response(modifiedPage)
+ return new Response(modifiedPage);
},
};
```
@@ -244,7 +340,7 @@ This will save the `modifiedPage` content in the `GAW_PAGE_KV` using the `reques
```diff
+ const options = {
-+ expirationTtl: 60 * 60 * 6 // Store the page for 6 hours
++ expirationTtl: 60 * 60 * 6 // Store the page for 6 hours
+ }
- await savePageToKv(env, request.url, modifiedPage)
@@ -258,17 +354,19 @@ Now that we are storing a modified version of the page for 24 hours, we can star
In the code below, we first check if there is page stored in the `GAW_PAGE_KV` using the key of `country.url`. If there is, we return that, otherwise we generate a modified page, store it, and return it.
```js
-import { savePageToKv, fetchPageFromKv } from "@greenweb/gaw-plugin-cloudflare-workers";
+import {
+ savePageToKv,
+ fetchPageFromKv,
+} from "@greenweb/gaw-plugin-cloudflare-workers";
export default {
async fetch(request, env, ctx) {
-
- const storedPage = await fetchPageFromKv(env, request.url)
+ const storedPage = await fetchPageFromKv(env, request.url);
if (storedPage) {
- return new Response(storedPage)
+ return new Response(storedPage);
}
-
+
const modifiedPage = `
@@ -278,12 +376,12 @@ export default {
This page has changed.
- `
+ `;
- await saveDataToKv(env, request.url, modifiedPage)
+ await saveDataToKv(env, request.url, modifiedPage);
// If we've got data back using the Grid-aware Websites library, let's return that to the browser
- return new Response(modifiedPage)
+ return new Response(modifiedPage);
},
};
```
diff --git a/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md b/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md
index 92c7353..d7cacc5 100644
--- a/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md
+++ b/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md
@@ -32,7 +32,14 @@ You should have:
You should also be aware of the limits and pricing of Cloudflare Workers, available on the [Cloudflare website](https://developers.cloudflare.com/workers/platform/).
-{% include 'partials/gaw-emaps-note.njk' %}
+