A place to cache linked articles (think custom and personal wayback machine)
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  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>
  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>Dropping Support For IE11 Is Progressive Enhancement (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="#f0f0ea">
  24. <meta name="msapplication-config" content="/static/david/icons2/browserconfig.xml">
  25. <meta name="theme-color" content="#f0f0ea">
  26. <!-- Documented, feel free to shoot an email. -->
  27. <link rel="stylesheet" href="/static/david/css/style_2021-01-20.css">
  28. <!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
  29. <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>
  30. <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>
  31. <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>
  32. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  33. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  34. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  35. <script>
  36. function toggleTheme(themeName) {
  37. document.documentElement.classList.toggle(
  38. 'forced-dark',
  39. themeName === 'dark'
  40. )
  41. document.documentElement.classList.toggle(
  42. 'forced-light',
  43. themeName === 'light'
  44. )
  45. }
  46. const selectedTheme = localStorage.getItem('theme')
  47. if (selectedTheme !== 'undefined') {
  48. toggleTheme(selectedTheme)
  49. }
  50. </script>
  51. <meta name="robots" content="noindex, nofollow">
  52. <meta content="origin-when-cross-origin" name="referrer">
  53. <!-- Canonical URL for SEO purposes -->
  54. <link rel="canonical" href="https://blog.carlmjohnson.net/post/2020/time-to-kill-ie11/">
  55. <body class="remarkdown h1-underline h2-underline h3-underline em-underscore hr-center ul-star pre-tick">
  56. <article>
  57. <header>
  58. <h1>Dropping Support For IE11 Is Progressive Enhancement</h1>
  59. </header>
  60. <nav>
  61. <p class="center">
  62. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  63. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-home"></use>
  64. </svg> Accueil</a> •
  65. <a href="https://blog.carlmjohnson.net/post/2020/time-to-kill-ie11/" title="Lien vers le contenu original">Source originale</a>
  66. </p>
  67. </nav>
  68. <hr>
  69. <p><em>TL;DR if you have to choose, you should prioritize users with no JavaScript over users with old JavaScript.</em></p>
  70. <p>If you’re a web developer working today, it’s probably long passed time for you to <strong>stop transpiling your modern JavaScript into ES5 for Internet Explorer</strong>. Any time or effort spent getting your JavaScript working in IE11 is wasted time that could be better spent <strong>making a better experience for users without JavaScript</strong>. Moving your resources from Internet Explorer to users without JavaScript will let you improve the SEO and accessibility of your site while providing a better experience for everyone.</p>
  71. <p>First, some notes about <em>who this advice is for</em>: I am assuming you’re working for a small team with no Quality Assurance testing department making a content focused site. If you have a large team that actually QA tests IE11, this advice may not apply to your situation. If you’re just doing personal projects for fun, you should have already dropped ES5 years ago. If you’re making more of an app than a content site, you probably should have cut off IE11 years ago, but the calculations are more complex and site specific.</p>
  72. <p><hr><h2 id="why-drop-support-for-ie11-javascript">Why drop support for IE11 JavaScript?</h2><p>To begin with, Internet Explorer represents <strong>less than 1%</strong> of my work traffic according to Google Analytics. People will sometime spin this away by arguing that 1% of a large number is also a large number. If you have 100 million hits, 1% is 1 million hits! But the flipside is that 99 million hits are <em>not</em> Internet Explorer and are not helped by optimizations aimed at Internet Explorer. What’s more this number has been <strong>falling steadily</strong>. Even just in the year and a half that my work site has been around it has dropped. It used to be closer to 2%. On top of that, outside of corporate/enterprise settings, most of these computers browsing your site with Internet Explorer will <strong>also have Chrome installed</strong>, and approximately 100% of the users will <strong>also own a smart phone</strong> they can use to browse your site if their computer cannot display it correctly. The computers in question will be old and have <strong>terrible performance</strong> assuming you do get the JavaScript to run in the first place.</p><p>Let’s contrast this with other users we could optimize for. The number of <strong>visually impaired</strong> readers is steady and will not go down absent some breakthrough in medical technology. Visually impaired readers can’t just use another device if the site doesn’t work in the device they have in front of them. In fact, even users who have physically normal vision are impaired when driving or walking, and it seems to me inevitable that as <strong>voice assistants</strong> become ubiquitous and easy to use, sighted users having websites read aloud to them will eventually become commonplace. Plus, there are <a href="https://arstechnica.com/tech-policy/2019/10/accessibility-the-future-and-why-dominos-matters/">legal imperatives</a> to make reasonable accommodations for visually impaired readers.</p><p>Users <strong>without JavaScript</strong> are another important consideration. As Jake Archibald said, <a href="https://www.twitter.com/jaffathecake/status/207096228339658752">“All your users are non-JS while they’re downloading your JS.”</a> And remember that sometimes, your user’s connection will drop out, and <a href="https://kryogenix.org/code/browser/everyonehasjs.html">the JavaScript will never load</a>. But beyond that, browsers like Opera Mini and extensions like NoScript or overly aggressive ad blockers are already about as common as users of Internet Explorer, and they’re not going to go away. It’s hard to estimate the number of people with JavaScript broken or disabled, but <a href="https://deliberatedigital.com/blockmetry/javascript-disabled">it appears to be stable from year to year</a>, because the underlying causes aren’t subject to change, unlike IE11 usage. Year over year, old computers will be replaced, and IE11 will fade. Disliking ads and having bad connections on the other hand are here to stay. Then there are the most important users of your site without JavaScript: <strong>web spiders and search engines</strong>. While Google does in theory use headless browsers to scrape sites while executing JavaScript, in practice, you’ll still get better SEO if you optimize your content to work without it, and there are other non-Google spiders that you may want crawling your site too, like the <a href="https://cloudinary.com/blog/a_primer_on_microbrowsers_tips_and_tricks_for_managing_the_seo_feedback_loop">microbrowsers</a> which add link previews to chat and social media.</p><p>If you’re running a small site without a QA team, it’s probably worth going to <a href="https://www.browserling.com">Browserling</a> right now and finding out if you’re even working in Internet Explorer to begin with. Browserling will let you run a virtual PC with Internet Explorer from your own browser for a limited time for free, so it’s probably easiest way to do a quick low effort <a href="https://en.wikipedia.org/wiki/Smoke_testing_(software)">smoke test</a> of your Internet Explorer support without having to install anything. Just open up a tab, take a look at your site, and see if the results surprise you.</p><p>I’ve seen developers (including most especially myself) burned by just <em>assuming</em> that <a href="https://babeljs.io">Babel</a> and <a href="https://github.com/postcss/autoprefixer">Autoprefixer</a> have solved all their problems with IE11. You may already be dropping support for IE11, you just don’t know it yet. I’ve had sites I work on break for months at a time in Internet Explorer with no complaints from readers. One site I redesigned got just a single reader inquiry about a widget that broke in IE11 after a redesign. When I told the reader to try loading the page in Chrome, he seemed satisfied. More recently, IE11 has had some terrible rendering bugs on my site for days or months at a time that have triggered <em>zero</em> reader complaints. If I don’t QA it myself, no one else will do it for me.</p><p>If you think your code will work but haven’t actually QA tested it, it can lead to the worst of both worlds: code bloated with extras for IE11 that still doesn’t even work anyway. It’s not enough to transpile down to ES5, you also need to polyfill every missing DOM API you use, and that can be a very complicated and bloated proposition.</p><p>Essentially, there are three target web platforms you might want to support:</p><ol><li>Modern browsers</li><li>Browsers without JavaScript</li><li>Internet Explorer 11 (if you’re supporting &lt;11, 😱)</li></ol><p>If you try to support IE11 but aren’t actually QA testing it, you will probably end up with only modern browsers working. It is better to aim for both modern browser support and no-JS browser support and actually succeed than to try for IE11 support and silently fail.</p><hr><h2 id="what-should-you-do-instead-of-optimizing-ie11">What should you do instead of optimizing IE11?</h2><p>The web is built on the foundation of <strong>progressive enhancement</strong>. Deliver “good enough” service to legacy browsers, and save the enhancements for the bulk of your userbase.</p><p>In 2017, Philip Walton advocated the “<a href="https://philipwalton.com/articles/deploying-es2015-code-in-production-today/">module/nomodule</a>” pattern for JavaScript. This can be reduced to the “module/bare minimum” pattern today. Walton wrote:</p><blockquote><p>Most developers think of <code>&lt;script type="module"&gt;</code> as way to load ES modules (and of course this is true), but <code>&lt;script type="module"&gt;</code> also has a more immediate and practical use-case—loading regular JavaScript files with ES2015+ features and knowing the browser can handle it!</p><p>To put that another way, every browser that supports <code>&lt;script type="module"&gt;</code> also supports most of the ES2015+ features you know and love.</p></blockquote><p>Essentially, if you set the script element’s type attribute to an unknown value, browsers will just ignore the script. The effect of this is that IE11 will ignore the contents of any <code>&lt;script type="module"&gt;</code> tags. Modern browsers, on the other hand, will ignore the contents of <code>&lt;script nomodule&gt;</code> tags. This allows roughly targeting your JavaScript to its intended platform.</p><p>Walton goes on to advocate creating <strong>two versions of the same JavaScript bundle</strong> with Webpack. Send modern browsers a smaller, modules specific ES2015 version and older browsers a larger, transpiled and polyfilled ES5 version.</p><p>As I wrote above, I think just using Babel to turn your JavaScript into ES5 cannot be trusted to actually work without constant QA vigilance. A seemingly insignificant change in your code might silently break IE11 support, and you could be none the wiser for months or years unless you constantly recheck IE11. Instead of this futile strategy of pushing back the tide, I advocate sending IE11 users <strong>as little JavaScript as possible</strong>, ideally none, but practically speaking, <strong>probably just your ads and analytics</strong>, and then <strong>actually test</strong> that it works.</p><p>The first step is to decide what your organizational priorities are. These days most software engineers are familiar with the concept of the <a href="https://en.wikipedia.org/wiki/Minimum_viable_product">minimum viable product</a>. What’s the <em>minimum viable experience</em> that you’re willing to deliver to IE users? Maybe you’re not ready to write them off completely and just deliver a broken page and a “Best Viewed in Chrome” icon. But you may be willing to say that as long as they view your ads, you don’t care if they can bypass the paywall or can’t comment on articles. In my case, I want Internet Explorer using readers to be able to read my articles and to show up in my analytics. Everything else, I’m willing to cut or sacrifice in the name of freeing up the resources for creating a better no-JS experience. If that’s my minimum viable experience, I am going to create it and test that it actually works, and then <strong>stop iterating</strong>, so that I don’t accidentally break it with my future changes to the enhanced, modern browser experience.</p><p>It may seem like more work to craft a separate experience for ES5 users instead of just backporting your modern JS, but it’s really not. The whole point of modern frontend JavaScript is that everything is broken into small modules that are imported and bundled in development. So, <a href="https://github.com/spotlightpa/poor-richard/blob/ab0ad83/src/esbuild/nomodules.js">here is the whole of the entrypoint</a> for my ES5/IE11 build:</p><div class="highlight"><pre class="chroma"><code class="language-javascript" data-lang="javascript"><span class="kr">import</span> <span class="s2">"../utils/add-listeners.js"</span><span class="p">;</span></p>
  73. <p><span class="c1">// eslint-disable-next-line no-console
  74. </span><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">warn</span><span class="p">(</span><span class="s2">"could not load enhancements"</span><span class="p">);</span>
  75. <span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="s2">"has-old-js"</span><span class="p">);</span>
  76. </code></pre></div><p>Once the import is loaded, Babel’d, and minifed, it comes to 17KB (real KB, not <a href="https://twitter.com/carlmjohnson/status/1069968685606031360">moon weight</a><i>!</i>). Essentially all the listeners do are add <a href="https://github.com/jehna/ga-lite">some analytics</a> and the JavaScript to make the menu hamburger button work. The <code>.has-old-js</code> class triggers a CSS utility class to show and hide things conditionally. A <code>&lt;noscript&gt;</code> tag in the HTML does the same, as does an <code>onerror</code> handler on the module script. This lets me show one experience to modern browsers and a secondary fallback experience to everyone else. Because this code is essentially set in stone, I don’t have to worry about new JavaScript features subtly breaking IE11 behind my back. Because it’s just an ordinary <code>&lt;noscript&gt;</code> tag, I can test it in my ordinary browser by just turning off JavaScript briefly. I can just <strong>QA it once</strong> and then trust it to keep working as new features roll out on the site. As long as IE11 users continue to show up in my analytics results, I will know that it’s still working.</p><p>The enhanced version of my JavaScript entrypoint imports the same listeners file, naturally, but it also imports about a dozen different little enhancements modules as well. It ends up being 51KB, which is 3 times larger than the IE11 version, but still 40% smaller than jQuery. (Thanks, <a href="https://fullstackradio.com/132">Alpine.js</a><em>!</em>) It’s bundled by <a href="https://esbuild.github.io">ESBuild</a> and takes advantage of minification tricks like keeping template literals and arrow functions. Running it through Babel for ES5 transpilation <strong>adds about 30%</strong> to the size even without any polyfills. In his testing with his own blog’s JavaScript, Philip Walton found that the ES2015+ version was around half the size of the ES5 version. The important thing is that dropping ES5 support to the minimum viable experience saves resources for all of your users, whether they use a modern browser or not.</p><hr><h2 id="concrete-techniques-taken-from-my-work-site">Concrete techniques taken from my work site</h2><p>Like all news sites these days, <a href="http://www.spotlightpa.org/">Spotlight PA</a> has a big modal screen that prompts you to sign up for our newsletter as you start reading an article. (Yes, yes, complain about it all you want, people actually do sign up for the newsletter because of this prompt. That’s why we show it.) What to do for IE11? That’s easy: nothing. As useful as the prompt is in getting new readers for our newsletter, there’s no reason to worry that 1% of readers potentially aren’t seeing the prompt in one browser. We have other text indicating that there’s a newsletter and most likely the readers will come back in a better browser, like their phones, and see the modal then. 1% growth in our newsletter is easily swamped by the noise from week to week, and there are many better ways to increase newsletter growth than chasing IE11 users. The first major way of doing progressive enhancement is to just <strong>omit unnecessary site enhancements</strong>.</p><p>Suppose you do end up at <a href="http://www.spotlightpa.org/newsletters/">spotlightpa.org/newsletters</a> with IE11. On that page we show little graphics promoting each of the different newsletters and when you click the graphic, it grows to show the sign up form for a particular newsletter. How can you replicate that without JavaScript? The second major way of doing progressive enhancement is <strong>to selectively show a static layout</strong>. Instead of clicking to reveal a form, the no-JS experience is that the hidden boxes are already expanded. I have two utility classes, <code>.no-js-only</code> and <code>.no-js-hide</code>, and I can use these to selectively show or hide an element based on whether it is applicable or inapplicable to a no JS user. The page looks a bit cluttered with all the sign up forms already revealed, but it’s still essentially usable.</p><figure><img src="/post/img/2020-dropdown.gif" alt="Screenshot showing one column disclosed"><figcaption><p>Regular /newsletters/ page</p></figcaption></figure><figure><img src="/post/img/2020-dropdown-all.png" alt="Screenshot showing all columns disclosed"><figcaption><p>No-JS /newsletters/ page</p></figcaption></figure><p>Similarly, at the bottom of an article, we have a list of three related articles and a “Read More” button. If you have modern JavaScript, the Read More button uses AJAX to add more article links and thumbnails to the list. If you don’t have JavaScript, the “Read More” button is a link to the news archive. The third major way of doing progressive enhancement is <strong>adding links when you can’t rely on dynamic HTML</strong>.</p><p>The search button on our site opens a modal that works via AJAX with <a href="https://www.algolia.com">Algolia</a>, but if you don’t have JavaScript, it becomes a link to <a href="https://www.spotlightpa.org/search/">spotlightpa.org/search</a> which just provides you with a form that submits a <code>site:www.spotlightpa.org</code> prefixed search to Google. The fourth technique then is to <strong>get someone else to solve your problem</strong>. As another example of this technique, we had a donation form written in Vue that fell back to sending readers to the donation processor’s default site. We had written the Vue form as a frontend to their payments gateway because the payment processing site was kind of clunky and ugly, but it was good enough as a last resort.</p><p>The advantage of all of these techniques is that when it comes time to test the JavaScript-less version of your site, all you need to do is temporarily turn off JavaScript in the browser you already use for development, instead of jumping through hoops to run a dying browser in an environment that is good enough to do actual development instead of mere smoke testing.</p><p>With a few minor omissions and links, you can create a site that <strong>works great in modern browsers with ES6+</strong> <em>and</em> <strong>acceptably in browsers without JavaScript</strong>. This approach is more sustainable for teams without the resources for extensive QA, and more beneficial to users of nonstandard browsers. Trying to recreate functionality that already works in modern browsers in IE11 is thankless work that is doomed to neglect. Creatively finding new ways to achieve adequate functionality without JavaScript is challenging and fun. You’ll be surprised at what you come up with.</p><p>The best way to help your IE11 users is to provide a great experience for your non-JS users and share that experience with them, instead of sending them an untested and buggy experience that also slows the experience of users with modern browsers.</p></p>
  77. </article>
  78. <hr>
  79. <footer>
  80. <p>
  81. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  82. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-home"></use>
  83. </svg> Accueil</a> •
  84. <a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
  85. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-rss2"></use>
  86. </svg> RSS</a> •
  87. <a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
  88. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-user-tie"></use>
  89. </svg> Pro</a> •
  90. <a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
  91. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-mail"></use>
  92. </svg> Email</a> •
  93. <abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
  94. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-hammer2"></use>
  95. </svg> Légal</abbr>
  96. </p>
  97. <template id="theme-selector">
  98. <form>
  99. <fieldset>
  100. <legend><svg class="icon icon-brightness-contrast">
  101. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-brightness-contrast"></use>
  102. </svg> Thème</legend>
  103. <label>
  104. <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
  105. </label>
  106. <label>
  107. <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
  108. </label>
  109. <label>
  110. <input type="radio" value="light" name="chosen-color-scheme"> Clair
  111. </label>
  112. </fieldset>
  113. </form>
  114. </template>
  115. </footer>
  116. <script>
  117. function loadThemeForm(templateName) {
  118. const themeSelectorTemplate = document.querySelector(templateName)
  119. const form = themeSelectorTemplate.content.firstElementChild
  120. themeSelectorTemplate.replaceWith(form)
  121. form.addEventListener('change', (e) => {
  122. const chosenColorScheme = e.target.value
  123. localStorage.setItem('theme', chosenColorScheme)
  124. toggleTheme(chosenColorScheme)
  125. })
  126. const selectedTheme = localStorage.getItem('theme')
  127. if (selectedTheme && selectedTheme !== 'undefined') {
  128. form.querySelector(`[value="${selectedTheme}"]`).checked = true
  129. }
  130. }
  131. const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
  132. window.addEventListener('load', () => {
  133. let hasDarkRules = false
  134. for (const styleSheet of Array.from(document.styleSheets)) {
  135. let mediaRules = []
  136. for (const cssRule of styleSheet.cssRules) {
  137. if (cssRule.type !== CSSRule.MEDIA_RULE) {
  138. continue
  139. }
  140. // WARNING: Safari does not have/supports `conditionText`.
  141. if (cssRule.conditionText) {
  142. if (cssRule.conditionText !== prefersColorSchemeDark) {
  143. continue
  144. }
  145. } else {
  146. if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
  147. continue
  148. }
  149. }
  150. mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
  151. }
  152. // WARNING: do not try to insert a Rule to a styleSheet you are
  153. // currently iterating on, otherwise the browser will be stuck
  154. // in a infinite loop…
  155. for (const mediaRule of mediaRules) {
  156. styleSheet.insertRule(mediaRule.cssText)
  157. hasDarkRules = true
  158. }
  159. }
  160. if (hasDarkRules) {
  161. loadThemeForm('#theme-selector')
  162. }
  163. })
  164. </script>
  165. </body>
  166. </html>