A place to cache linked articles (think custom and personal wayback machine)
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

index.html 22KB

3 år sedan
3 år sedan
3 år sedan
3 år sedan
3 år sedan
3 år sedan
3 år sedan
3 år sedan
3 år sedan
3 år sedan
3 år sedan
3 år sedan
3 år sedan
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  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>I bought 300 emoji domain names from Kazakhstan and built an email service (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://tinyprojects.dev/projects/mailoji">
  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>I bought 300 emoji domain names from Kazakhstan and built an email service</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://tinyprojects.dev/projects/mailoji" title="Lien vers le contenu original">Source originale</a>
  67. </p>
  68. </nav>
  69. <hr>
  70. <p>TLDR; I bought 300 emoji domain names from Kazakhstan and built an <a href="https://mailoji.com" target="_blank">emoji email address service</a>. In the process I went viral on TikTok, made $1000 in a week, hired a Japanese voice actor, and learnt about the weird world of emoji domains.</p>
  71. <h3>🌅 The setup</h3>
  72. <p>Not long ago I decided it would be a brilliant idea to buy the domain name <a href="/posts/i_bought_netflix_dot_soy" target="_blank">netflix.soy</a>.</p>
  73. <p>Whilst arguably there are better ways to spend £17, I did learn a lot about domain names, including that it's possible to have emoji domains like <span class="domain">😊.ws</span>.</p>
  74. <p>It's pretty hard to go a day without seeing an emoji somewhere on the internet. Yet, I'd never seen an emoji domain name before.</p>
  75. <p>I wondered:</p>
  76. <p>Could I buy an emoji domain name?</p>
  77. <h3>💸 Buying an emoji domain name</h3>
  78. <p>My goal was to buy a single character emoji domain name, like 💡 or 🍰. I didn't know what I'd do with it, I just wanted to see if I could get one.</p>
  79. <p>I found a website that showed every available emoji domain for 4 different extensions.</p>
  80. <p>Sadly, nearly every single one had been registered. I was late to the party.</p>
  81. <p><img src="https://i.gyazo.com/6aa39e2f0e8e2f9039b61c95ea360cb7.png" alt="Taken emoji domains"></p>
  82. <p>A simple mailbox emoji with a .ws extension was still available though, so I bought it.</p>
  83. <h3>📪 The mailbox</h3>
  84. <p><img src="https://i.gyazo.com/1b78a0aab3ca0b04649eacf46fd3bc0a.png" alt="Mailbox emoji domain in GoDaddy"></p>
  85. <p><span class="domain">📪.ws</span> was now mine. Mission complete.</p>
  86. <p>I set up a website and felt rather accomplished with my tiny mailbox.</p>
  87. <p>I could've stopped there and called it a day. But, then I had another thought:</p>
  88. <p>Could I use my little mailbox emoji domain in an email address?</p>
  89. <p>That'd be pretty cute.</p>
  90. <h3>✉️ Emoji mail attempt #1</h3>
  91. <p>I gave it a go. I setup an email forwarder to route all email sent to <span class="domain">📪.ws</span> to my regular email address.</p>
  92. <p>Eagerly I typed <span class="domain">ben@📪.ws</span> into the "to" field of gmail and hit send.</p>
  93. <p><img src="https://i.gyazo.com/3ab67d13bcfad786b7f3ba9a01968272.png" alt="Sending first emoji mail"></p>
  94. <h3>🛑 Blocked</h3>
  95. <p>The email never hit my inbox. It was lost forever in cyberspace.</p>
  96. <p>Turns out emoji domain names score very highly for spam and were going to be blocked to high heaven.</p>
  97. <p>But, it was interesting that I could send mail towards an emoji email address.</p>
  98. <p>So I wondered:</p>
  99. <p>If a normal .com email address doesn't get blocked for spam, could I route my emoji mail through that?</p>
  100. <h3>💌 Emoji mail attempt #2</h3>
  101. <p>It would work like this: </p>
  102. <ol>
  103. <li>Email sent to <span class="domain">ben@📪.ws</span></li>
  104. <li><span class="domain">ben@📪.ws</span> forwards to <span class="domain">nospam@normal.com</span></li>
  105. <li><span class="domain">nospam@normal.com</span> forwards to my email address and won't get blocked.</li>
  106. </ol>
  107. <p>I cobbled together something using AWS, and tried my experiment again.</p>
  108. <p>
  109. </p>
  110. <p>to: <span class="domain">ben@📪.ws</span></p>
  111. <pre><code>&lt;p&gt;message: Hi Ben, how's it going?&lt;/p&gt;
  112. </code></pre>
  113. <p>Send.</p>
  114. <p><img src="https://i.gyazo.com/bdc529a214b0e2019a96a86c933c4bcb.png" alt="First emoji mail"></p>
  115. <p>It worked!</p>
  116. <h3>🧨 Where things started to get out of control</h3>
  117. <p>At this point I was inclined to stop and write a post about emoji email addresses. I'd had a good run.</p>
  118. <p>But then I wondered:</p>
  119. <p>My mailbox emoji email address is great and all, but do you know what would be better? <span class="domain">ben@⭐</span></p>
  120. <p>Now how do I get one of those?</p>
  121. <h3>🎣 Emoji domain name hunting</h3>
  122. <p>Only 13 TLDs in the world accept registrations of emoji domain names: .cf, .ga, .gq, .la, .ml, .tk, .st, .fm, .to, .je, .gg, .kz, and .ws.</p>
  123. <p>The website I had used to purchase <span class="domain">📪.ws</span> only showed 4 TLDs: .fm, .ws, .to and .ml. These are considered the gold standard of emoji domain name registrars.</p>
  124. <p>Every emoji had been taken on these though. You could of course get multi-character emoji domains like <span class="domain">🎉🐢.ws</span>, but I wanted single character emoji domains only.</p>
  125. <p>So I wondered:</p>
  126. <p>Do any of those other TLDs have any emoji domains left?</p>
  127. <h3>🔭 The great hunt</h3>
  128. <p>I already had some code that performed WHOIS lookups to see if a domain name is available for a list of TLDs.</p>
  129. <p>Previously I'd used this code to buy <a href="/posts/i_bought_netflix_dot_soy" target="_blank">facebook.网站</a>, only for Marky Z to snatch it back from me. Cheeky bugger.</p>
  130. <p>I booted up the code and loaded in some A-tier emojis (e.g. ⭐,😂,❤️) and the 13 TLDs that accepted them.</p>
  131. <p><i>&gt;node search.js</i> [ENTER]</p>
  132. <h3>🎁 The results</h3>
  133. <p><img src="https://i.gyazo.com/6adaeb53f45f75605749f78d4dcac8c3.png" alt="Console output showing available TLDs for emoji domains"></p>
  134. <p>Instantly I was seeing results! .la, .ga, .gq, .je. There were plenty of emojis still out there on these alternative extensions.</p>
  135. <p>An extension that stood out to me straight away was .gg, for the Island of Guernsey. "GG" is an acronym for "Good Game", and I say it often when I lose at online games. It was perfect.</p>
  136. <p><span class="domain">⭐.gg</span> was available for €29. I hit purchase.</p>
  137. <h3>💔 No GG for me</h3>
  138. <p>The next day Guernsey sent me an email.</p>
  139. <p><img src="https://i.gyazo.com/527c4941fbb3ddcffbc92a0c2ef1c828.png" alt="Email from Guernsey saying emoji domains not available on .gg"></p>
  140. <p>Long story short, although you could register emoji domain names with them, they didn't actually work.</p>
  141. <p>Good game Guernsey. Back to the drawing board.</p>
  142. <h3>⭐ Crazy for KZ</h3>
  143. <p>With every other extension I kept hitting walls. A lot of the registrars wouldn't even let me search for emoji domains. Nothing was working.</p>
  144. <p>One extension that kept cropping up was .kz of Kazakhstan. But, I headed over to their registar website and it was entirely in Russian.</p>
  145. <p><img src="https://i.gyazo.com/6c20c8eea912beb320555c0dace78b1b.png" alt="Russian domain name website"></p>
  146. <p>I do not speak Russian.</p>
  147. <p>Using Google Translate, I tried to navigate the website and buy a .kz emoji domain.</p>
  148. <p>It was a long, painful process. But, after phoning my bank to confirm I was indeed trying to make a purchase using Kazakhstani tenge, <span class="domain">⭐.kz</span> was sitting in my account.</p>
  149. <p>I plugged it into my email system and <span class="domain">ben@⭐.kz</span> worked.</p>
  150. <p>Very nice.</p>
  151. <h3>💼 Let's start an email service</h3>
  152. <p>Something excited me. Nearly all single character emojis were available on .kz, and they were only $8 each.</p>
  153. <p>So, I wondered:</p>
  154. <p>What if you could get an email address with any emoji you wanted?</p>
  155. <p>I pictured email addresses like <span class="domain">bob@🚀</span>, <span class="domain">alice@🌸</span>, <span class="domain">melvin@🍆</span>.</p>
  156. <p>All I'd need to do is buy every emoji domain to build a service like this.</p>
  157. <p>It was insane, but it was possible.</p>
  158. <h3>🌙 The night of 150 emojis</h3>
  159. <p>I decided I was going to do it.</p>
  160. <p>If I was chuffed with my mailbox emoji email address, perhaps others would be too.</p>
  161. <p>I got out my debit card, and, one by one, started buying Kazakhstan emoji domains.</p>
  162. <p><span class="domain">💡.kz</span>, <span class="domain">👑.kz</span>, <span class="domain">🌈.kz</span>, <span class="domain">😎.kz</span>. Buy, buy, buy, buy.</p>
  163. <p>It was slightly painful watching my bank account going down, and the number of emoji domains go up.</p>
  164. <p>80 emojis in, forking over money for a goat emoji domain name, you seriously start to question what you're doing.</p>
  165. <p>$1200 later, 150 emoji domains were mine.</p>
  166. <h3>💻 Building an emoji email address website</h3>
  167. <p>Finally, I needed a website where you could register an emoji email address and it would forward mail like <span class="domain">ben@📪.ws</span> did.</p>
  168. <p>Using vanilla HTML, JS and CSS, plus Stripe's API for payments, I cobbled together an MVP over a few weeks.</p>
  169. <p>Once it was done, I bought one last domain name: <a href="https://mailoji.com" target="_blank">mailoji.com</a>. My new emoji email address service <a href="https://mailoji.com" target="_blank">Mailoji</a> was ready. Get your emoji email addresses.</p>
  170. <p><img src="https://i.gyazo.com/30c54b0bed4ffc85cb38940a2b26748e.png" alt="Mailoji emoji email address website"></p>
  171. <h3>📱 TikTok</h3>
  172. <p>I'd gone from being curious about emoji domain names to now owning 150 of Kazakhstan's finest.</p>
  173. <p>The next step was to convince someone else to buy an emoji email address.</p>
  174. <p>TikTok seemed like a good place to start given its demographic. So, I recorded a short video advert and started a "TikTok for business" application to publish it.</p>
  175. <p>On the final page of the application I was asked for a VAT registration number. Mailoji was not a proper business yet, so there was no way I could publish my ad.</p>
  176. <p>Screw it, I'll post the video normally.</p>
  177. <p>Upload.</p>
  178. <h3>🎉 First sales</h3>
  179. <p>Here is the <a href="https://www.tiktok.com/@mailoji/video/6925405275201539334?lang=en&amp;is_copy_url=1&amp;is_from_webapp=v3" target="_blank">advert</a> if you're interested.</p>
  180. <p>The video sat at 0 views for about 5 hours before the TikTok algorithm started to work its magic.</p>
  181. <p>Slowly, the views started ramping up. 500 views, to 5k views, to 50k views. It was incredible to witness.</p>
  182. <p>People were loving emoji email addresses, people were hating emoji email addresses.</p>
  183. <p>It was like Marmite, a talking point. None of it mattered though because emoji email addresses were selling! <span class="domain">@🚀</span>, <span class="domain">@📷</span> &amp; <span class="domain">@💻</span> addresses were the most popular.</p>
  184. <p>Over 2 days the TikTok video reached 200k+ views, and 60 emoji email addresses had been sold netting ~$300/yr in revenue.</p>
  185. <p><img src="https://i.gyazo.com/e33fb288704cc42f58e50ff4a5d33fa8.png" alt="Stripe graph showing first sales from selling emoji domains"></p>
  186. <p>I took this as a fantastic indicator. So guess what I did?</p>
  187. <h3>💵 Buying more emoji domain names</h3>
  188. <p>I decided to purchase 100 more emoji domains.</p>
  189. <p>I cried into my keyboard forking out yet more money for a llama emoji that I probably didn't need.</p>
  190. <p>In the end I had 250 emoji domains. If there was ever a moat into the emoji email address world, this was it.</p>
  191. <h3>📅 Preparing for launch</h3>
  192. <p>I figured the more people with emoji email addresses, the more people who would see them, and the more people who would buy them. A beautiful cycle.</p>
  193. <p>My next goal was a Product Hunt launch to get exposure for Mailoji, and kickstart this cycle.</p>
  194. <p>I prepped my launch post, carefully choosing each word and image.</p>
  195. <p>I even created this over-hyped promotional video, complete with Japanese voice actor saying the words "Mailoji".</p>
  196. <p class="video-container">
  197. <iframe src="https://www.youtube.com/embed/JKxEXZv4G3c" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen>VIDEO</iframe></p>
  198. <p>Mailoji was ready for launch.</p>
  199. <h3>🚀 Launch Day</h3>
  200. <p>At 12:03 AM PST Mailoji went live on <a href="https://www.producthunt.com/posts/mailoji" target="_blank">Product Hunt</a>. We had come a long way from that little mailbox emoji.</p>
  201. <p><img src="https://i.gyazo.com/0cf1d62b2d820c8f456a95da3cffda01.png" alt="Product hunt launch thumbnail"></p>
  202. <p>It was 8:03 AM UK time. Bleary eyed, with a cup of tea in hand, I watched as Mailoji did battle.</p>
  203. <p>I had chosen to launch on a Wednesday against some stiff competition, but Mailoji really held its own.</p>
  204. <p>At the end of the day it finished in 5th place. Here were the end of day stats: </p>
  205. <ul>
  206. <li>🌎 6.7k website views
  207. </li><li>💌 150+ emoji email addresses sold</li>
  208. <li>💵 $830/yr ARR</li>
  209. <li>🔺 320 upvotes</li>
  210. <li>🏅 5th place on Product Hunt</li>
  211. <li>🎀 Most popular Mailoji: @🚀</li>
  212. </ul>
  213. <p>Over 150 emoji email addresses were sold in a day, and I received some fantastic feedback from the Product Hunt community.</p>
  214. <p>It was done, Mailoji had officially launched.</p>
  215. <h3>📙 The aftermath</h3>
  216. <p>I wish this story ended with Mailoji blowing up and the queen registering an emoji email address or something (I'll reserve <span class="domain">Liz@👑.kz</span> just in case).</p>
  217. <p>But, currently Mailoji is sitting at ~$1440/year in revenue. There's now 300 emoji domains to choose from though.</p>
  218. <p><img src="https://i.gyazo.com/6eac0eb3bcdaa1ee78e57b6692ed9afc.png" alt="Final Stripe ARR chart for Mailoji"></p>
  219. <p>Even though I still haven't made the money back on all the emoji domains I bought, creating an emoji email address service was so much fun.</p>
  220. <p>It was an adventure. A rabbit hole containing multiple rabbit holes.</p>
  221. <p>This project started out as an exploration into emoji domain names; a weird, forgotten about internet feature that I've now become quite fond of.</p>
  222. <p>Yes emoji domains are hard to type on desktop, yes there's too many variations, and yes, most form validations hate them.</p>
  223. <p>But they're fun, and I think tech should be more fun.</p>
  224. <p>Thanks for reading. If you want to get in touch, I've got a brand new email address at the bottom of this website.</p>
  225. </article>
  226. <hr>
  227. <footer>
  228. <p>
  229. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  230. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
  231. </svg> Accueil</a> •
  232. <a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
  233. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-rss2"></use>
  234. </svg> Suivre</a> •
  235. <a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
  236. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-user-tie"></use>
  237. </svg> Pro</a> •
  238. <a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
  239. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-mail"></use>
  240. </svg> Email</a> •
  241. <abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
  242. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-hammer2"></use>
  243. </svg> Légal</abbr>
  244. </p>
  245. <template id="theme-selector">
  246. <form>
  247. <fieldset>
  248. <legend><svg class="icon icon-brightness-contrast">
  249. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-brightness-contrast"></use>
  250. </svg> Thème</legend>
  251. <label>
  252. <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
  253. </label>
  254. <label>
  255. <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
  256. </label>
  257. <label>
  258. <input type="radio" value="light" name="chosen-color-scheme"> Clair
  259. </label>
  260. </fieldset>
  261. </form>
  262. </template>
  263. </footer>
  264. <script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
  265. <script>
  266. function loadThemeForm(templateName) {
  267. const themeSelectorTemplate = document.querySelector(templateName)
  268. const form = themeSelectorTemplate.content.firstElementChild
  269. themeSelectorTemplate.replaceWith(form)
  270. form.addEventListener('change', (e) => {
  271. const chosenColorScheme = e.target.value
  272. localStorage.setItem('theme', chosenColorScheme)
  273. toggleTheme(chosenColorScheme)
  274. })
  275. const selectedTheme = localStorage.getItem('theme')
  276. if (selectedTheme && selectedTheme !== 'undefined') {
  277. form.querySelector(`[value="${selectedTheme}"]`).checked = true
  278. }
  279. }
  280. const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
  281. window.addEventListener('load', () => {
  282. let hasDarkRules = false
  283. for (const styleSheet of Array.from(document.styleSheets)) {
  284. let mediaRules = []
  285. for (const cssRule of styleSheet.cssRules) {
  286. if (cssRule.type !== CSSRule.MEDIA_RULE) {
  287. continue
  288. }
  289. // WARNING: Safari does not have/supports `conditionText`.
  290. if (cssRule.conditionText) {
  291. if (cssRule.conditionText !== prefersColorSchemeDark) {
  292. continue
  293. }
  294. } else {
  295. if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
  296. continue
  297. }
  298. }
  299. mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
  300. }
  301. // WARNING: do not try to insert a Rule to a styleSheet you are
  302. // currently iterating on, otherwise the browser will be stuck
  303. // in a infinite loop…
  304. for (const mediaRule of mediaRules) {
  305. styleSheet.insertRule(mediaRule.cssText)
  306. hasDarkRules = true
  307. }
  308. }
  309. if (hasDarkRules) {
  310. loadThemeForm('#theme-selector')
  311. }
  312. })
  313. </script>
  314. </body>
  315. </html>