Web component that shows estimated CO₂e per page view, its source and measurement status. It has zero runtime dependencies, light/dark themes, three visual variants and a production ESM bundle of about 10 KB gzipped (measured after NODE_ENV=production npm run build; CI budget is 10.5 KB).
The badge uses CometWeb and documents a SWDM v4 first-load lite approximation. It is not a full visitor/cache model, an ESG certificate or a claim of a web-wide percentile. Product page: cometweb.io/carbon-badge.
For a website owner, use a published CometWeb snapshot. It is a cheap, stable read for visitors: it does not start a scan, the result is dated and the proof link points to the published measurement.
This release is 1.0.10. npm still serves 1.0.6 until tag v1.0.10 is published. Git tag v1.0.9 never reached the registry. Pin the embed to this version. The integrity hash is from NODE_ENV=production npm run build && npm run sri on this commit. https://cometweb.io/scripts/carbon-badge/ is a separate site copy and does not serve this file.
<script
type="module"
src="https://unpkg.com/@cometweb/carbon-badge@1.0.10/dist/cometweb-carbon-badge.esm.js"
integrity="sha384-oH1uwtH4vymZPLdy+3pCQkOGgrT3gilrMT+ZPv/4VKU/x4C+antJkZK1FYZWnJSc"
crossorigin="anonymous"></script>
<cometweb-carbon-badge
snapshot-id="<published_public_id>"
theme="light">
</cometweb-carbon-badge>Equivalent jsDelivr URL: https://cdn.jsdelivr.net/npm/@cometweb/carbon-badge@1.0.10/dist/cometweb-carbon-badge.esm.js.
Replace <published_public_id> with the lowercase hexadecimal public ID from the published CometWeb ecology snapshot. Keep mode unset: a snapshot-id automatically selects snapshot mode. Do not set mode="api" or mode="estimate" together with snapshot-id — that combination fails closed (N/D) instead of silently estimating a local grade. Use live api mode only when you explicitly want URL-based measurement and omit snapshot-id.
Snapshot results include their measurement date, freshness deadline, method, formula ID, status and evidence URL. Expired, revoked, missing or partial data is never labelled as verified and never becomes a trustworthy letter grade.
| Light | Dark | No measurement |
|---|---|---|
![]() |
![]() |
![]() |
These are component screenshots with synthetic fixture data, not measurements of a live website.
| Variant | Light | Dark |
|---|---|---|
default — card |
![]() |
![]() |
compact — small strip |
![]() |
![]() |
minimal — footer signature |
![]() |
![]() |
<cometweb-carbon-badge variant="compact" theme="light" mode="estimate"></cometweb-carbon-badge>
<cometweb-carbon-badge variant="minimal" theme="dark" mode="estimate"></cometweb-carbon-badge>Omit variant for the default card. All variants retain the estimate, source/status and attribution. minimal has a transparent background: use the light theme on a light surface and dark on a dark surface. Changing the variant does not fetch a new measurement.
The code in this repository is version 1.0.9. Build it locally when you need the exact behavior documented here:
git clone https://github.com/CometWeb-io/Carbon-Badge.git
cd Carbon-Badge
npm ci
npm run buildCopy dist/cometweb-carbon-badge.esm.js to your site's public assets, for example /vendor/, then add:
<script type="module" src="/vendor/cometweb-carbon-badge.esm.js"></script>
<cometweb-carbon-badge theme="light" mode="estimate"></cometweb-carbon-badge>For a bundler, install the built package (npm install /path/to/Carbon-Badge) and use import '@cometweb/carbon-badge';.
| Mode | Data source | Setup |
|---|---|---|
snapshot |
Published CometWeb measurement | Set snapshot-id; recommended owner path |
estimate |
Current page's browser transfer data and simplified SWDM v4 calculation | Explicitly set mode="estimate" (do not also set snapshot-id) |
api (default) |
CometWeb public carbon-badge endpoint | Network access; optionally set url (do not also set snapshot-id) |
An unavailable measurement displays N/D, not a made-up A+ score. A remote URL in API mode never falls back to estimating the host page. API mode is an external service dependency; the component's MIT license does not guarantee service availability.
<cometweb-carbon-badge
snapshot-id="<published_public_id>"
variant="compact"
theme="light">
</cometweb-carbon-badge>green-host is a self-declared hosting assertion in estimate mode. It is not checked against a registry and does not change the local letter grade. Letter grades are fixed product bands; a percentile appears only when supplied by the API.
| Attribute | Default | Description |
|---|---|---|
url |
current page URL | Page to measure in API mode; public identity is origin + pathname (query stripped by default) |
snapshot-id |
— | Published CometWeb public ID; selects snapshot mode when mode is omitted. Conflicts with mode="api" / mode="estimate" (fails closed) |
mode |
api or snapshot with ID |
snapshot — published result; api — live public API; estimate — client-side SWDM v4 first-load lite. Do not combine api/estimate with snapshot-id |
variant |
default |
default card, compact strip or minimal transparent signature; changes do not reload data |
theme |
dark |
Allowlisted: dark or light |
cache-ttl |
720 |
Client cache TTL in minutes for API/estimate mode, capped by the server valid_until deadline |
green-host |
false |
Set "true" in estimate mode when the host is green-powered |
allow-query |
— | Optional comma-separated query keys to keep in public URL identity (default: strip all query) |
api-url / api-key are not public attributes. The badge talks only to https://app.cometweb.io/api.
snapshotfetchesGET /public/carbon-badge/id/{public_id}. It never starts a URL scan. Invalid, missing, mismatching, malformed, stale, partial or revoked snapshots render N/D. The client requires the response to identify the same published snapshot with valid measurement and freshness dates.apifetchesGET /public/carbon-badge. Invalid or empty payloads render N/D. A remoteurlnever falls back to estimating the host page.estimatewaits for page quiescence, measures transfer with Resource Timing, then appliesswdm-v4-lite-first-load-v1. DOM size is never used as a carbon score. Missing timing renders N/D.
Cheap published snapshot read on the server: GET /api/public/carbon-badge/id/{public_id} — no HTTP re-scan.
| Condition | Footer text |
|---|---|
Published snapshot returns verified: true, status: ready, a public ID, fresh dates, and an evidence URL bound to /carbon-badge/{publicId} on an allowlisted CometWeb origin |
Verified by CometWeb |
| Estimate mode, stale/partial/revoked/unknown result, or untrusted/missing evidence | Powered by CometWeb |
Letters are the CometWeb Carbon Score (carbon-badge-bands-v1) — product bands, not the public Digital Carbon Rating Scale.
| CometWeb Score | CO₂e / visit | Meaning |
|---|---|---|
| A+ | < 0.10 g | Exceptionally clean |
| A | < 0.20 g | Very clean |
| B | < 0.40 g | Band B |
| C | < 0.70 g | Band C |
| D | < 1.00 g | Band D |
| F | ≥ 1.00 g | High emissions |
CO₂e = data_GB × (E_operational + E_embodied) × grid_intensity
E_operational = 0.055 × (1 − greenHostingFactor) + 0.059 + 0.080 kWh/GB
E_embodied = 0.012 + 0.013 + 0.081 kWh/GB
grid_intensity = 494 gCO₂e/kWh
data_GB uses bytes / 1,000,000,000. In estimate mode, green-host="true" is recorded as a self-declared assertion but does not change greenHostingFactor (always 0 in local estimate mode). See the full reference for the complete contract.
| Event | When |
|---|---|
cometweb:badge-load |
Data rendered (detail: url, co2Grams, score, measurementSource, retrievalSource, status, formulaId, scoreModelId, …) |
cometweb:badge-error |
Measurement failed (detail: sanitized url, mode, reason, status) |
document.querySelector('cometweb-carbon-badge')
?.addEventListener('cometweb:badge-load', (event) => {
console.log(event.detail.score, event.detail.co2Grams, event.detail.status);
});npm ci
npm run build
npm test
npm run typecheck
npm run typecheck:test
npm run clean && NODE_ENV=production npm run build
npm run size:check
npm run security:audit
npm run pack:consumer
npm run test:e2e
npm pack --dry-runPull requests also run CodeQL and dependency review. Pushes to main produce a CycloneDX SBOM and an attested package artifact; the workflow does not publish to npm automatically.
See CHANGELOG.md for release notes.






