A place to cache linked articles (think custom and personal wayback machine)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.html 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. <!doctype html><!-- This is a valid HTML5 document. -->
  2. <!-- Screen readers, SEO, extensions and so on. -->
  3. <html lang="fr">
  4. <!-- Has to be within the first 1024 bytes, hence before the `title` element
  5. See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
  6. <meta charset="utf-8">
  7. <!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
  8. <!-- The viewport meta is quite crowded and we are responsible for that.
  9. See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
  10. <meta name="viewport" content="width=device-width,initial-scale=1">
  11. <!-- Required to make a valid HTML5 document. -->
  12. <title>CO2.js: An Open Library for Digital Carbon Reporting (archive) — David Larlet</title>
  13. <meta name="description" content="Publication mise en cache pour en conserver une trace.">
  14. <!-- That good ol' feed, subscribe :). -->
  15. <link rel="alternate" type="application/atom+xml" title="Feed" href="/david/log/">
  16. <!-- Generated from https://realfavicongenerator.net/ such a mess. -->
  17. <link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons2/apple-touch-icon.png">
  18. <link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons2/favicon-32x32.png">
  19. <link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons2/favicon-16x16.png">
  20. <link rel="manifest" href="/static/david/icons2/site.webmanifest">
  21. <link rel="mask-icon" href="/static/david/icons2/safari-pinned-tab.svg" color="#07486c">
  22. <link rel="shortcut icon" href="/static/david/icons2/favicon.ico">
  23. <meta name="msapplication-TileColor" content="#f7f7f7">
  24. <meta name="msapplication-config" content="/static/david/icons2/browserconfig.xml">
  25. <meta name="theme-color" content="#f7f7f7" media="(prefers-color-scheme: light)">
  26. <meta name="theme-color" content="#272727" media="(prefers-color-scheme: dark)">
  27. <!-- Documented, feel free to shoot an email. -->
  28. <link rel="stylesheet" href="/static/david/css/style_2021-01-20.css">
  29. <!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
  30. <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
  31. <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
  32. <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
  33. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  34. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  35. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  36. <script>
  37. function toggleTheme(themeName) {
  38. document.documentElement.classList.toggle(
  39. 'forced-dark',
  40. themeName === 'dark'
  41. )
  42. document.documentElement.classList.toggle(
  43. 'forced-light',
  44. themeName === 'light'
  45. )
  46. }
  47. const selectedTheme = localStorage.getItem('theme')
  48. if (selectedTheme !== 'undefined') {
  49. toggleTheme(selectedTheme)
  50. }
  51. </script>
  52. <meta name="robots" content="noindex, nofollow">
  53. <meta content="origin-when-cross-origin" name="referrer">
  54. <!-- Canonical URL for SEO purposes -->
  55. <link rel="canonical" href="https://branch.climateaction.tech/issues/issue-4/normalising-digital-carbon-reporting/">
  56. <body class="remarkdown h1-underline h2-underline h3-underline em-underscore hr-center ul-star pre-tick" data-instant-intensity="viewport-all">
  57. <article>
  58. <header>
  59. <h1>CO2.js: An Open Library for Digital Carbon Reporting</h1>
  60. </header>
  61. <nav>
  62. <p class="center">
  63. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  64. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
  65. </svg> Accueil</a> •
  66. <a href="https://branch.climateaction.tech/issues/issue-4/normalising-digital-carbon-reporting/" title="Lien vers le contenu original">Source originale</a>
  67. </p>
  68. </nav>
  69. <hr>
  70. <p>Uploading and downloading the bits and bytes that make up the internet uses <em>a lot</em> of electricity. Breaking the internet down to a systems level, data transfer over networks accounts for an <a href="https://sustainablewebdesign.org/calculating-digital-emissions/">estimated 14% of the web’s total electricity</a> consumption. Networks are also globally distributed, meaning that the bytes you downloaded to render this web page in your browser are probably passed through several different electricity grids. Those grids are made up of different mixes of green and fossil fuel energy.</p>
  71. <p>Being able to measure and account for the emissions of digital services is increasingly important, especially as carbon emissions reporting becomes a mandated part of business operations. Outside of the corporate space, consumers are also increasingly demanding greater visibility of CO2 information for the goods and services they use.</p>
  72. <p>In order to meet the growing demands for reporting and transparency, developers need a way to measure the carbon emissions associated with the apps, sites, and software they build. On the server side, we’re seeing more providers build carbon reporting into their platforms. However, on the application side, it’s largely up to developers themselves to implement solutions. That’s where libraries like CO2.js come in handy, providing a set of research-based, standard calculations that enable developers to quickly add carbon awareness to their products and projects.</p>
  73. <h2>What is CO2.js?</h2>
  74. <p><a href="https://github.com/thegreenwebfoundation/co2.js">CO2.js</a> is a JavaScript library that allows developers to estimate the emissions associated with their apps, websites and software. At its core, CO2.js takes an input of data, in bytes, and returns an estimate of the carbon emissions produced to move that data over the internet. It can be run in Node.js server environments, in the browser, as well as on some serverless and edge compute runtimes.</p>
  75. <h2><strong>Why use it?</strong></h2>
  76. <p>Being able to estimate the carbon emissions associated with digital activities can be of benefit to both development teams and end users. The carbon emissions of the internet are something that is abstract, and out of sight. Using CO2.js allows these emissions to be surfaced, visualised, and presented in ways that make it easier for people to comprehend and act on.</p>
  77. <p>The possible uses for CO2.js are wide and varied. It can be used in user-facing applications to give visibility to the carbon impact of user activity in the application. Users uploading files, or downloading content, could be notified of the impacts of those tasks. Large, carbon-intensive, data transfers could also be blocked or limited. Users could also have the option to set a carbon budget for their browsing or use of an app, website, or online service.</p>
  78. <p>Behind the scenes, developers could look to use CO2.js as part of their deployment workflow. In the same way that web developers might set a <em>performance budget</em> for their site, <a href="https://css-tricks.com/reduce-your-websites-environmental-impact-with-a-carbon-budget/">a carbon budget could also be used</a>. If a website or app exceeds a threshold for carbon intensity, then an alert can be raised or a new deployment can be blocked. The data from CO2.js can also be used as part of internal monitoring tools and dashboards.</p>
  79. <p>Office managers and sustainability teams could also use CO2.js to track the carbon intensity of data usage within an office environment. Plugging network data usage into CO2.js can allow for monitoring and reporting on the digital usage footprint of an organisation or business.</p>
  80. <h2>CO2.js today</h2>
  81. <p>CO2.js can do more than <em>just</em> estimate the carbon impact of data transfer on the internet. It also includes functions that use The Green Web Foundation’s <em>greencheck</em> API to determine if a website or domain is hosted on a green web host.</p>
  82. <p>In the wild, the CO2.js package already receives close to 2000 weekly downloads on NPM. It is actively used by website testing tools like <a href="https://ecograder.com/">Ecograder</a>, and performance tools like <a href="http://SiteSpeed.io">SiteSpeed.io</a> to track and display website carbon emissions for users. Website analytics service <a href="https://withcabin.com/">Cabin</a> also use CO2.js to calculate emissions for page views and historical analysis of CO2 for each page.</p>
  83. <figure class="wp-block-image size-large"><img loading="lazy" src="https://branch.climateaction.tech/wp-content/uploads/2022/08/ximage-8-1024x428.png.pagespeed.ic.1HClVH965i.png" alt="" class="wp-image-1698" srcset="https://branch.climateaction.tech/wp-content/uploads/2022/08/ximage-8-1024x428.png.pagespeed.ic.1HClVH965i.png 1024w, https://branch.climateaction.tech/wp-content/uploads/2022/08/ximage-8-300x125.png.pagespeed.ic.9qZR3hPWHI.png 300w, https://branch.climateaction.tech/wp-content/uploads/2022/08/ximage-8-768x321.png.pagespeed.ic.glvEqo2nt8.png 768w, https://branch.climateaction.tech/wp-content/uploads/2022/08/ximage-8-1536x641.png.pagespeed.ic.nnVe1_DjTF.png 1536w, https://branch.climateaction.tech/wp-content/uploads/2022/08/ximage-8.png.pagespeed.ic.zEH60AU5VC.png 2000w" sizes="(max-width: 1024px) 100vw, 1024px"><figcaption>Screenshot for the <a href="http://sitespeed.io/" target="_blank" rel="noreferrer noopener">SiteSpeed.io</a> sustainability plugin.</figcaption></figure>
  84. <figure class="wp-block-image size-large"><img loading="lazy" src="https://branch.climateaction.tech/wp-content/uploads/2022/08/ximage-9-1024x547.png.pagespeed.ic.slGUmxUU6_.png" alt="" class="wp-image-1699" srcset="https://branch.climateaction.tech/wp-content/uploads/2022/08/ximage-9-1024x547.png.pagespeed.ic.slGUmxUU6_.png 1024w, https://branch.climateaction.tech/wp-content/uploads/2022/08/image-9-300x160.png.pagespeed.ce.LL_EBYDoT1.png 300w, https://branch.climateaction.tech/wp-content/uploads/2022/08/ximage-9-768x410.png.pagespeed.ic.HgzumbgWzi.png 768w, https://branch.climateaction.tech/wp-content/uploads/2022/08/ximage-9.png.pagespeed.ic.sMcfWZTX9Y.png 1497w" sizes="(max-width: 1024px) 100vw, 1024px"><figcaption>Screenshot of a <a href="http://sitespeed.io/" target="_blank" rel="noreferrer noopener">SiteSpeed.io</a> dashboard in Grafana.</figcaption></figure>
  85. <figure class="wp-block-image size-large"><img loading="lazy" src="https://branch.climateaction.tech/wp-content/uploads/2022/09/xcabin-screenshot-1024x771.png.pagespeed.ic.-Sd-9Uwv0Y.png" alt="Screenshot of the Cabin website analytics dashboard showing page view carbon emissions." class="wp-image-1934" srcset="https://branch.climateaction.tech/wp-content/uploads/2022/09/xcabin-screenshot-1024x771.png.pagespeed.ic.-Sd-9Uwv0Y.png 1024w, https://branch.climateaction.tech/wp-content/uploads/2022/09/xcabin-screenshot-300x226.png.pagespeed.ic.MYG9q4FK8s.png 300w, https://branch.climateaction.tech/wp-content/uploads/2022/09/xcabin-screenshot-768x578.png.pagespeed.ic.zLfxHplGB2.png 768w, https://branch.climateaction.tech/wp-content/uploads/2022/09/xcabin-screenshot-1536x1156.png.pagespeed.ic.b5hLWwBd-t.png 1536w, https://branch.climateaction.tech/wp-content/uploads/2022/09/xcabin-screenshot.png.pagespeed.ic.4geNSFc233.png 2000w" sizes="(max-width: 1024px) 100vw, 1024px"><figcaption>Screenshot of the Cabin website analytics dashboard showing page view carbon emissions.</figcaption></figure>
  86. <p>CO2.js is covered by an Apache 2.0 license. This allows for the library to be used in digital tools and services. Since the <a href="https://github.com/thegreenwebfoundation/co2.js">code is kept on GitHub</a>, and is also open for anyone to review, fork, modify, and contribute to. As we’ll touch on a little later, any datasets and figures used in CO2.js are also open, keeping with the themes of open-source, open data, and Open Climate.</p>
  87. <h3>Models available for estimating digital carbon</h3>
  88. <p>There are a few different models that can be used to measure digital carbon emissions. CO2.js includes two of these – the OneByte model, and the Sustainable Web Design model.</p>
  89. <p><strong>Sustainable Web Design</strong></p>
  90. <p>By default, CO2.js uses the <a href="https://sustainablewebdesign.org/calculating-digital-emissions">Sustainable Web Design model</a> developed by a collaboration of <a href="https://www.wholegraindigital.com/">Wholegrain Digital</a>, <a href="https://www.mightybytes.com/">Mightybytes</a>, <a href="https://www.medina-works.com/">Medina Works</a>, <a href="https://ecoping.earth/">EcoPing</a>, and the <a href="https://www.thegreenwebfoundation.org/">Green Web Foundation</a>. It is designed for helping understand the environmental impact of websites, as well as digital products and services.</p>
  91. <p>This model segments the system (the internet) into four parts – data centres, networks, end-user devices, and device production. Based on the bytes passed to it, the Sustainable Web Design model calculates the energy used by each part of the system. These figures are then converted to carbon estimates using the global carbon intensity of electricity from the <strong><a href="https://ember-climate.org/insights/research/european-electricity-review-2022/">Ember annual global electricity review</a></strong>.</p>
  92. <figure class="wp-block-image"><img src="https://fershad.notion.site/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F1aa832d9-d1ba-4486-93a0-6d08a0ffa7ff%2FUntitled.png?table=block&amp;id=000ea413-3041-4f09-9e2a-2277d69ba268&amp;spaceId=b4cfdeca-5b5d-4325-a3da-7980b01e614c&amp;width=1920&amp;userId=&amp;cache=v2" alt=""><figcaption>The breakdown of system segments used for calculations in the Sustainable Web Design model.</figcaption></figure>
  93. <p>The carbon intensity of electricity figures used by the Sustainable Web Design model includes full lifecycle emissions. This includes upstream methane, supply-chain and manufacturing emissions, and includes all gases, converted into CO2 equivalent over a 100-year timescale.</p>
  94. <p><strong>OneByte Model</strong></p>
  95. <p>Additionally, CO2.js also allows developers to use the OneByte model introduced by The Shift Project in their report on CO2 emissions from digital infrastructure, <strong><a href="https://theshiftproject.org/en/lean-ict-2/">Lean ICT: for a sober digital</a></strong>. This model returns a number for the estimated carbon emissions given the number of bytes sent over the wire. It has been used for estimating the impact of video streaming, file downloads and websites.</p>
  96. <p><strong>How the models differ</strong></p>
  97. <p>These models return slightly different results since they apply different system boundaries as part of their calculations. Tom Greenwood has written <a href="https://www.wholegraindigital.com/blog/website-energy-consumption/">a terrific blog post</a> explaining system boundaries and how they impact carbon estimates.</p>
  98. <p>The OneByte model, as it has been implemented in CO2.js, applies narrow system boundaries – datacenter and network only. It takes a top-down approach to calculations, returning a single carbon emissions result based on a given input. It should be noted that the original model used in the Lean ICT report did have broader systems boundaries. However, when the model was included in CO2.js a judgement call was made to reduce its scope. You can read more about why in <a href="https://github.com/thegreenwebfoundation/co2.js/issues/68">this GitHub issue</a>.</p>
  99. <p>On the other hand, the Sustainable Web Design model has a broader system boundary (explained above). It takes a more complex, but detailed, bottom-up approach. By using a wider system boundary, the Sustainable Web Design model provides a more comprehensive carbon estimate. This also means that segmented estimates can be produced for each part of the system, allowing for greater granularity and flexibility.</p>
  100. <p>As a result, the carbon estimates returned when using the OneByte model will be lower than those from Sustainable Web Design for the same amount of data transfer.</p>
  101. <h2>What’s planned for CO2.js?</h2>
  102. <p>CO2.js will evolve alongside the continued research into the environmental impact of the digital sector. As new data and methodologies emerge, the library will be updated to provide the best possible source of digital carbon estimates for developers. Being an <a href="https://github.com/thegreenwebfoundation/co2.js/issues">open-source library</a>, contributions from the community are always welcome.</p>
  103. <p>Alongside this, making it easier for developers to start using CO2.js is a key priority. Building flexibility into the library, so that it can be used across JavaScript environments and frameworks, will hopefully empower more developers to build carbon intelligence into their tools, platforms, and services. Work on this has already begun, with the <a href="https://github.com/thegreenwebfoundation/co2.js/blob/main/CHANGELOG.md#0100-2022-06-27">recent v0.10.0 release of CO2.js</a> making it much easier to use the library in both node and the browser with ESM, CJS and IIFE build.</p>
  104. <p>In the near term, there are a few more updates planned:</p>
  105. <h3>Sensible and extensible defaults</h3>
  106. <p>Each carbon estimation model comes with a set of constants that calculate electricity use and carbon emissions. Allowing users to be able to adjust these constants will enable more contextually accurate results to be returned. For example, a developer whose app runs on a server based in Norway should be able to update the CO2 per kilowatt-hour constant to reflect the carbon intensity of the Norwegian electric grid.</p>
  107. <p>Taking it further, it may also be possible for developers to use the Sustainable Web Design model to update the constants for website caching and return visitors. This would allow them to generate website carbon estimates that are more accurate and appropriate for the site they are analysing.</p>
  108. <h3>Bring in average and marginal carbon intensity data</h3>
  109. <p>Sourcing carbon intensity data shouldn’t be the remit of developers. For that reason, CO2.js now includes yearly average grid intensity data from <a href="https://ember-climate.org/data/data-explorer/">Ember</a>, as well as marginal intensity data from the <a href="https://unfccc.int/">UNFCCC</a> (United Nations Framework Convention on Climate Change).</p>
  110. <p>Average emissions intensity uses the fuel mix of the entire electricity grid and can be used to derive estimates for the carbon footprint of a digital product or service. You’ll see average intensity used in the majority of carbon reporting standards and tooling. This makes it useful if you were to use CO2.js to feed in data to other carbon reporting tools.</p>
  111. <p>Marginal intensity, on the other hand, looks at where the additional electricity to power a device, product or service would come from. In almost all cases it would be from a fossil-fuel power source, and so marginal intensity figures tend to be higher than average intensity figures. The <a href="https://greensoftware.foundation/">Green Software Foundation</a> is one group that uses marginal intensity as part of its specification.</p>
  112. <p>The team over at Electricity Maps have two great blog posts <a href="https://electricitymaps.com/blog/marginal-emissions-what-they-are-and-when-to-use-them/">explaining the concepts</a> and why you might <a href="https://electricitymaps.com/blog/marginal-vs-average-real-time-decision-making/">use one over the other</a>.</p>
  113. <p>Enabling developers to access this data, and use it with the models available in CO2.js, is a key step toward generating more accurate carbon estimates.</p>
  114. <h3>Reducing the barrier to use</h3>
  115. <p>To accompany these changes, <a href="https://developers.thegreenwebfoundation.org/">a new developer documentation website</a> has been created for CO2.js and some of the other open-source libraries maintained by The Green Web Foundation. Providing an easy-to-digest, accessible set of tutorials and guides will hopefully enable more developers to start using CO2.js in their work. The documentation site will grow with time and as new capabilities are added to the library.</p>
  116. <h2>Making carbon reporting accessible</h2>
  117. <p>The possible uses for CO2.js are wide and varied, and the data produced can play an important role in educating stakeholders and enabling more climate-conscious decisions to be made when creating digital products and services.</p>
  118. <p>If you are using (or do end up using) CO2.js in production then The Green Web Foundation would love to hear from you! Use the <a href="https://www.thegreenwebfoundation.org/support-form/">contact form</a> on their website to get in touch.</p>
  119. </article>
  120. <hr>
  121. <footer>
  122. <p>
  123. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  124. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
  125. </svg> Accueil</a> •
  126. <a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
  127. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-rss2"></use>
  128. </svg> Suivre</a> •
  129. <a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
  130. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-user-tie"></use>
  131. </svg> Pro</a> •
  132. <a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
  133. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-mail"></use>
  134. </svg> Email</a> •
  135. <abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
  136. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-hammer2"></use>
  137. </svg> Légal</abbr>
  138. </p>
  139. <template id="theme-selector">
  140. <form>
  141. <fieldset>
  142. <legend><svg class="icon icon-brightness-contrast">
  143. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-brightness-contrast"></use>
  144. </svg> Thème</legend>
  145. <label>
  146. <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
  147. </label>
  148. <label>
  149. <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
  150. </label>
  151. <label>
  152. <input type="radio" value="light" name="chosen-color-scheme"> Clair
  153. </label>
  154. </fieldset>
  155. </form>
  156. </template>
  157. </footer>
  158. <script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
  159. <script>
  160. function loadThemeForm(templateName) {
  161. const themeSelectorTemplate = document.querySelector(templateName)
  162. const form = themeSelectorTemplate.content.firstElementChild
  163. themeSelectorTemplate.replaceWith(form)
  164. form.addEventListener('change', (e) => {
  165. const chosenColorScheme = e.target.value
  166. localStorage.setItem('theme', chosenColorScheme)
  167. toggleTheme(chosenColorScheme)
  168. })
  169. const selectedTheme = localStorage.getItem('theme')
  170. if (selectedTheme && selectedTheme !== 'undefined') {
  171. form.querySelector(`[value="${selectedTheme}"]`).checked = true
  172. }
  173. }
  174. const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
  175. window.addEventListener('load', () => {
  176. let hasDarkRules = false
  177. for (const styleSheet of Array.from(document.styleSheets)) {
  178. let mediaRules = []
  179. for (const cssRule of styleSheet.cssRules) {
  180. if (cssRule.type !== CSSRule.MEDIA_RULE) {
  181. continue
  182. }
  183. // WARNING: Safari does not have/supports `conditionText`.
  184. if (cssRule.conditionText) {
  185. if (cssRule.conditionText !== prefersColorSchemeDark) {
  186. continue
  187. }
  188. } else {
  189. if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
  190. continue
  191. }
  192. }
  193. mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
  194. }
  195. // WARNING: do not try to insert a Rule to a styleSheet you are
  196. // currently iterating on, otherwise the browser will be stuck
  197. // in a infinite loop…
  198. for (const mediaRule of mediaRules) {
  199. styleSheet.insertRule(mediaRule.cssText)
  200. hasDarkRules = true
  201. }
  202. }
  203. if (hasDarkRules) {
  204. loadThemeForm('#theme-selector')
  205. }
  206. })
  207. </script>
  208. </body>
  209. </html>