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.

преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 3 години
преди 2 години
преди 3 години
преди 2 години
преди 3 години
преди 2 години
преди 3 години
преди 3 години
преди 2 години
преди 3 години
преди 2 години
преди 3 години
преди 2 години
преди 3 години
преди 2 години
преди 3 години
преди 3 години
преди 3 години
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>