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 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  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>Clever Code Considered Harmful (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. <!-- Is that even respected? Retrospectively? What a shAItshow…
  28. https://neil-clarke.com/block-the-bots-that-feed-ai-models-by-scraping-your-website/ -->
  29. <meta name="robots" content="noai, noimageai">
  30. <!-- Documented, feel free to shoot an email. -->
  31. <link rel="stylesheet" href="/static/david/css/style_2021-01-20.css">
  32. <!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
  33. <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>
  34. <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>
  35. <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>
  36. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  37. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  38. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  39. <script>
  40. function toggleTheme(themeName) {
  41. document.documentElement.classList.toggle(
  42. 'forced-dark',
  43. themeName === 'dark'
  44. )
  45. document.documentElement.classList.toggle(
  46. 'forced-light',
  47. themeName === 'light'
  48. )
  49. }
  50. const selectedTheme = localStorage.getItem('theme')
  51. if (selectedTheme !== 'undefined') {
  52. toggleTheme(selectedTheme)
  53. }
  54. </script>
  55. <meta name="robots" content="noindex, nofollow">
  56. <meta content="origin-when-cross-origin" name="referrer">
  57. <!-- Canonical URL for SEO purposes -->
  58. <link rel="canonical" href="https://www.joshwcomeau.com/career/clever-code-considered-harmful/">
  59. <body class="remarkdown h1-underline h2-underline h3-underline em-underscore hr-center ul-star pre-tick" data-instant-intensity="viewport-all">
  60. <article>
  61. <header>
  62. <h1>Clever Code Considered Harmful</h1>
  63. </header>
  64. <nav>
  65. <p class="center">
  66. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  67. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
  68. </svg> Accueil</a> •
  69. <a href="https://www.joshwcomeau.com/career/clever-code-considered-harmful/" title="Lien vers le contenu original">Source originale</a>
  70. </p>
  71. </nav>
  72. <hr>
  73. <p class="sc-e646da6e-0 buyZsM">There is something undeniably satisfying about coming up with clever solutions to hard problems. There is a joy when you challenge yourself to use recursion instead of iteration, for example, or when you create elegant, cascading layers of abstraction that ensure code is never duplicated.</p>
  74. <p class="sc-e646da6e-0 buyZsM">My favourite outlet for this kind of programming is <a href="https://projecteuler.net" rel="noopener noreferrer" target="_blank" class="sc-7af019d9-0 fZHCDu sc-53808c2b-0 NuBMO">Project Euler</a>.</p>
  75. <p class="sc-e646da6e-0 buyZsM">Project Euler is a repository of challenges based around advanced mathematics, meant to be solved with software. The catch is that your program should run in under a minute, on 2004-era hardware. That means that a brute-force solution often won’t cut it, and you’ll have to come up with a smarter solution.</p>
  76. <p class="sc-e646da6e-0 buyZsM">Here’s an example:</p>
  77. <p class="sc-e646da6e-0 buyZsM"><span type="default" class="sc-9a7b0d0-0 jvgAVO"><img src="/images/legacy/1__sbbO5gKvxk__TuJOIRMUzKA.png" alt="A math problem, to calculate numbers arranged in a spiral problem. Screenshot of this page: https://projecteuler.net/problem=58" class="sc-9a7b0d0-2 jkoJNm"><span class="sc-9a7b0d0-1 fbsvTj">Helpful tip: Euler is pronounced “Oiler”. Impress your math friends with this knowledge!</span></span></p>
  78. <p class="sc-e646da6e-0 buyZsM">Once you’ve solved a problem, you’re able to view solutions that other people have shared. And oh wow, do people come up with terse, clever solutions for these things.</p>
  79. <aside class="sc-3583c7cd-0 sc-3583c7cd-3 Nmjxf bsUafj"><p class="sc-3583c7cd-6 iESgn">I’m kinda breaking the Project Euler rules here— you’re not supposed to share solutions, to avoid spoiling it for others— but don’t worry, these solutions are totally indecipherable. You won’t spoil the challenge.</p></aside>
  80. <p class="sc-e646da6e-0 buyZsM">Here's one by a user named WillNess written in Haskell, a functional programming language:</p>
  81. <pre></pre>
  82. <p class="sc-e646da6e-0 buyZsM">Here's another one, written in <a href="https://en.wikipedia.org/wiki/J_%28programming_language%29" rel="noopener noreferrer" target="_blank" class="sc-7af019d9-0 fZHCDu sc-53808c2b-0 NuBMO">J lang</a> by user u56:</p>
  83. <pre></pre>
  84. <p class="sc-e646da6e-0 buyZsM">Clearly, it takes a tremendous amount of skill to solve such a hard problem with such a small amount of code. I would feel very pleased with myself if I was able to write a solution like this; my solutions are always way longer and less elegant.</p>
  85. <p class="sc-e646da6e-0 buyZsM">This is not "production-ready" code, though. This is recreational code. This is code that you write to feel clever, to impress fellow math nerds, to exercise your brain. When you solve a Project Euler problem, you never have to look at that code again. It's disposable. We aren't handing it off to someone else to maintain it.</p>
  86. <p class="sc-e646da6e-0 buyZsM">This is a different universe from the environments we write code in day-to-day.</p>
  87. <p class="sc-e646da6e-0 buyZsM">When it comes to day-to-day production code, here's the barometer I like to use: will a junior developer, someone at the very start of their career, struggle to understand this code?</p>
  88. <p class="sc-e646da6e-0 buyZsM">In the context of a shared codebase, good code is simple code. Code that doesn’t do anything fancy. Code that makes minimal use of abstractions. Code that you’d use to explain fundamental concepts to novices.</p>
  89. <p class="sc-e646da6e-0 buyZsM">A few years ago, I was asked to review a pull request that included the following function:</p>
  90. <pre></pre>
  91. <p class="sc-e646da6e-0 buyZsM">I suggested we re-write it like this:</p>
  92. <pre></pre>
  93. <p class="sc-e646da6e-0 buyZsM">The original version has lots of things going for it, on paper:</p>
  94. <ul class="sc-19724b12-1 hKaNYm"><li class="sc-19724b12-3 kuSaGk"><span class="sc-19724b12-5 jxFLLh"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg></span><p class="sc-19724b12-4 kJtFVp">Less code (only 4 statements instead of 7)</p></li><li class="sc-19724b12-3 kuSaGk"><span class="sc-19724b12-5 jxFLLh"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg></span><p class="sc-19724b12-4 kJtFVp">No duplication (the second version uses two <code class="sc-1a723291-0 YrISa">if</code> statements that do essentially the same thing)</p></li><li class="sc-19724b12-3 kuSaGk"><span class="sc-19724b12-5 jxFLLh"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg></span><p class="sc-19724b12-4 kJtFVp">Doesn't mutate any variables</p></li><li class="sc-19724b12-3 kuSaGk"><span class="sc-19724b12-5 jxFLLh"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg></span><p class="sc-19724b12-4 kJtFVp">More scalable: could easily be rewritten to handle 10 fields instead of 2.</p></li></ul>
  95. <p class="sc-e646da6e-0 buyZsM">And yet, the original version is <em class="sc-51e913c7-0 cWQfrg">way</em> harder to understand. I had to burn a ton of calories trying to work out what it was doing. I suspect many junior developers would be completely stumped trying to decipher it.</p>
  96. <p class="sc-e646da6e-0 buyZsM">In my opinion, the readability cost of the functional version is too high. It's not worth it.</p>
  97. <p class="sc-e646da6e-0 buyZsM">To understand why I think readability is such a crucial attribute of good code, let’s look at a popular open-source library, <a href="https://github.com/lodash/lodash" rel="noopener noreferrer" target="_blank" class="sc-7af019d9-0 fZHCDu sc-53808c2b-0 NuBMO">lodash</a>.</p>
  98. <p class="sc-e646da6e-0 buyZsM"><span type="default" class="sc-9a7b0d0-0 jvgAVO"><img src="/images/legacy/1__cDMSqwCi1aSPRxXdG5PUzg.png" alt="Screenshot of the “lodash” Github page" class="sc-9a7b0d0-2 jkoJNm"></span></p>
  99. <p class="sc-e646da6e-0 buyZsM">lodash is an immensely popular tool. It’s downloaded more than 26,000,000 times a week on NPM alone, and has over 42,000 Github stars. There is something absolutely curious about it, though; it consistently has less than 10 open issues.</p>
  100. <p class="sc-e646da6e-0 buyZsM">"Inbox zero" is a thing with email, but it <em class="sc-51e913c7-0 cWQfrg">never</em> happens with issues on popular projects. And yet, lodash often sits at zero open issues. It's been like this for years.</p>
  101. <p class="sc-e646da6e-0 buyZsM">Well, one reason is that the library’s primary author, John-David Dalton, is a passionate maintainer who spends a lot of his time triaging issues as they come in. But I don’t believe anyone, no matter how superhuman, can get a library this popular to 0 issues alone.</p>
  102. <p class="sc-e646da6e-0 buyZsM">Years ago, I heard JDD on <a href="https://podcasts.apple.com/us/podcast/012-jsair-lodash-open-source-with-john-david-dalton/id1066446588?i=1000364088851" rel="noopener noreferrer" target="_blank" class="sc-7af019d9-0 fZHCDu sc-53808c2b-0 NuBMO">a podcast</a> talk about how the key to managing a project like this is to encourage lots of folks to contribute to it. One of the ways he’s done this is by keeping the code at a pretty fundamental level; by using simple, basic constructs, it ensures that aspiring contributors can understand and contribute to the code, regardless of how much experience they have. I believe JDD mentions that they prefer if/else to ternaries simply because less people have experience with ternaries. When the goal is to keep the code simple, the expressive power of the ternary operator is detrimental.</p>
  103. <p class="sc-e646da6e-0 buyZsM">This is important to keep in mind if you’re building an open-source tool, but it’s <em class="sc-51e913c7-0 cWQfrg">even more important</em> if you’re working in a production codebase with other humans. Especially ones that have less experience than you.</p>
  104. <aside class="sc-3583c7cd-0 sc-3583c7cd-1 Nmjxf bvghBJ"><p class="sc-3583c7cd-5 crvSnl"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></p><strong class="sc-3583c7cd-4 ghVlSM">Zero issues no more</strong><div class="sc-3583c7cd-6 iESgn"><p class="sc-e646da6e-0 buyZsM">As I write this update in 2023, Lodash has a couple hundred open issues. This is likely because its maintainer, JDD, has moved onto other projects.</p></div></aside>
  105. <p class="sc-e646da6e-0 buyZsM">Have you ever heard someone say this?</p>
  106. <blockquote class="sc-4c838171-0 lkTYca"><p class="sc-e646da6e-0 buyZsM">Less code means less space for bugs to hide</p></blockquote>
  107. <p class="sc-e646da6e-0 buyZsM">This argument makes the case that short code is better, because it'll be easier to spot bugs. With every additional character you type, you're increasing the likelihood of a mistake.</p>
  108. <p class="sc-e646da6e-0 buyZsM">This is true when it comes to <em>typos</em>, sure. But typos tend to be easy to catch and fix. The <em class="sc-51e913c7-0 cWQfrg">really</em> troublesome bugs — the ones that tend to break user experiences for <em>weeks</em> as developers pass the support ticket around like a hot potato — are often caused by too much <em class="sc-51e913c7-0 cWQfrg">complexity</em>, not too many characters.</p>
  109. <p class="sc-e646da6e-0 buyZsM">In order to debug an issue, you have to wrap your mind around what the code is doing. When you create an abstraction to reduce duplication (“Make it DRY”), you add a layer of indirection that your mind has to unpack. The harder it is for your mental model to account for every edge-case and possible state, the more likely it is that you’ll have trouble diagnosing what’s gone wrong.</p>
  110. <blockquote class="sc-4c838171-0 lkTYca"><p class="sc-e646da6e-0 buyZsM">“Everyone knows that debugging is twice as hard as writing a program in the first place. So if you’re as clever as you can be when you write it, how will you ever debug it?”</p></blockquote>
  111. <p class="sc-e646da6e-0 buyZsM">— <a href="https://en.wikipedia.org/wiki/Brian_Kernighan" rel="noopener noreferrer" target="_blank" class="sc-7af019d9-0 fZHCDu sc-53808c2b-0 NuBMO">Brian Kernighan</a>, The Elements of Programming Style</p>
  112. <p class="sc-e646da6e-0 buyZsM">If the goal is to reduce complexity, and abstractions add complexity, should we abolish abstractions altogether?</p>
  113. <p class="sc-e646da6e-0 buyZsM">Well, no. Abstractions are everywhere. Loops are abstractions. Functions are abstractions. Programming languages themselves are abstractions over machine code, which itself is an abstraction over transistors flickering off and on really fast. It’s abstractions all the way down.</p>
  114. <p class="sc-e646da6e-0 buyZsM">The key is to weigh the cost of an abstraction against its benefit. Say we’re building a React app, and we have a list of 100 things to render. We could copy/paste the same JSX 100 times, or we could map over an array and write the JSX once. The “complex” solution in this case is totally worth it, because the underlying complexity is commonly known, and the alternative would be burdensome to maintain.</p>
  115. <p class="sc-e646da6e-0 buyZsM">As we build stuff, we make trade-off decisions like this all the time. If I have a point, it’s that we should consider these tradeoffs with our most junior teammates in mind; how much complexity are we adding for them? Is it worth it?</p>
  116. <p class="sc-e646da6e-0 buyZsM">Code sometimes has to be complex, because the real world is complex and our software has to model it! We won’t always be able to write code that a junior engineer can easily parse and contribute to. Sometimes the business logic is genuinely really tricky, sometimes we have to use an API with an inscrutable interface, and so on.</p>
  117. <p class="sc-e646da6e-0 buyZsM">I think the best way to deal with this is to try and sequester complexity. Set up clear boundaries between the simple stuff and the complex stuff. Don’t let the complexity seep into the surrounding areas.</p>
  118. <p class="sc-e646da6e-0 buyZsM">John-David Dalton did this with lodash. According to his interview in <a href="https://podcasts.apple.com/us/podcast/012-jsair-lodash-open-source-with-john-david-dalton/id1066446588?i=1000364088851" rel="noopener noreferrer" target="_blank" class="sc-7af019d9-0 fZHCDu sc-53808c2b-0 NuBMO">that same podcast</a>, the vast majority of lodash code is simple and easy-to-follow, and they’ve pushed the complex bits to a sophisticated core that handles the hard problems. This means that most contributors are spared from having to deal with that complexity, since it isn’t sprinkled across the application.</p>
  119. <p class="sc-e646da6e-0 buyZsM">If your app is architected so that the most complex concerns are all dealt with in the same place, you can keep the overwhelming majority of your app’s surface area simple.</p>
  120. <p class="sc-e646da6e-0 buyZsM">What if the junior engineer needs to work on that complex core? Well, good news! They have a more-senior person (you) to help guide them through it. Mentorship and education is a huge part of being a senior developer.</p>
  121. <p class="sc-e646da6e-0 buyZsM">One of my favourite talks from React Rally last year was <a href="https://www.youtube.com/watch?v=-NP_upexPFg" rel="noopener noreferrer" target="_blank" class="sc-7af019d9-0 fZHCDu sc-53808c2b-0 NuBMO">Chantastic’s “Hot Garbage; Clean Code is Dead”</a>. I won’t spoil the talk (seriously, go watch it!), but one of the takeaways I took from it is that everyone suffers from impostor syndrome, and as a result, we're always trying to prove to each other that we know our stuff. If we write a function that is super clever and indecipherable, our co-workers will know that we're smart, that we belong here!</p>
  122. <p class="sc-e646da6e-0 buyZsM">What I've come to realize, though, is that anyone can write code that seems complicated. The hard thing is solving complex problems with simple code. If you can develop that skill, nobody will ever doubt your abilities. ✨</p>
  123. </article>
  124. <hr>
  125. <footer>
  126. <p>
  127. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  128. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
  129. </svg> Accueil</a> •
  130. <a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
  131. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-rss2"></use>
  132. </svg> Suivre</a> •
  133. <a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
  134. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-user-tie"></use>
  135. </svg> Pro</a> •
  136. <a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
  137. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-mail"></use>
  138. </svg> Email</a> •
  139. <abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
  140. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-hammer2"></use>
  141. </svg> Légal</abbr>
  142. </p>
  143. <template id="theme-selector">
  144. <form>
  145. <fieldset>
  146. <legend><svg class="icon icon-brightness-contrast">
  147. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-brightness-contrast"></use>
  148. </svg> Thème</legend>
  149. <label>
  150. <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
  151. </label>
  152. <label>
  153. <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
  154. </label>
  155. <label>
  156. <input type="radio" value="light" name="chosen-color-scheme"> Clair
  157. </label>
  158. </fieldset>
  159. </form>
  160. </template>
  161. </footer>
  162. <script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
  163. <script>
  164. function loadThemeForm(templateName) {
  165. const themeSelectorTemplate = document.querySelector(templateName)
  166. const form = themeSelectorTemplate.content.firstElementChild
  167. themeSelectorTemplate.replaceWith(form)
  168. form.addEventListener('change', (e) => {
  169. const chosenColorScheme = e.target.value
  170. localStorage.setItem('theme', chosenColorScheme)
  171. toggleTheme(chosenColorScheme)
  172. })
  173. const selectedTheme = localStorage.getItem('theme')
  174. if (selectedTheme && selectedTheme !== 'undefined') {
  175. form.querySelector(`[value="${selectedTheme}"]`).checked = true
  176. }
  177. }
  178. const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
  179. window.addEventListener('load', () => {
  180. let hasDarkRules = false
  181. for (const styleSheet of Array.from(document.styleSheets)) {
  182. let mediaRules = []
  183. for (const cssRule of styleSheet.cssRules) {
  184. if (cssRule.type !== CSSRule.MEDIA_RULE) {
  185. continue
  186. }
  187. // WARNING: Safari does not have/supports `conditionText`.
  188. if (cssRule.conditionText) {
  189. if (cssRule.conditionText !== prefersColorSchemeDark) {
  190. continue
  191. }
  192. } else {
  193. if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
  194. continue
  195. }
  196. }
  197. mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
  198. }
  199. // WARNING: do not try to insert a Rule to a styleSheet you are
  200. // currently iterating on, otherwise the browser will be stuck
  201. // in a infinite loop…
  202. for (const mediaRule of mediaRules) {
  203. styleSheet.insertRule(mediaRule.cssText)
  204. hasDarkRules = true
  205. }
  206. }
  207. if (hasDarkRules) {
  208. loadThemeForm('#theme-selector')
  209. }
  210. })
  211. </script>
  212. </body>
  213. </html>