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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  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>How to Favicon in 2021: Six files that fit most needs (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://evilmartians.com/chronicles/how-to-favicon-in-2021-six-files-that-fit-most-needs">
  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>How to Favicon in 2021: Six files that fit most needs</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://evilmartians.com/chronicles/how-to-favicon-in-2021-six-files-that-fit-most-needs" title="Lien vers le contenu original">Source originale</a>
  67. </p>
  68. </nav>
  69. <hr>
  70. <article class="post__intro">
  71. <p>It is time to rethink how we cook a set of favicons for modern browsers and stop the icon generator madness. Currently, frontend developers have to deal with 20+ static PNG files just to display a tiny website logo in a browser tab or on a touchscreen. Read on to see how to take a smarter approach and adopt a minimal set of icons that fits most modern needs.</p>
  72. </article>
  73. <p>The favicons proved to be a more exhaustive topic that anyone could wish for, so I also summarized a whole article in just two code snippets for those who suffered enough and know exactly what to do. Still, I recommend geeking out to the rest of it!</p>
  74. <h2 id="extremely-short-version">Extremely short version</h2>
  75. <p>Instead of serving dozens of icons, all you need is just <strong>five</strong> icons and one JSON file.</p>
  76. <p>In your HTML, for the browser:</p>
  77. <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"icon"</span> <span class="na">href=</span><span class="s">"/favicon.ico"</span> <span class="na">sizes=</span><span class="s">"any"</span><span class="nt">&gt;</span><span class="c">&lt;!-- 32×32 --&gt;</span>
  78. <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"icon"</span> <span class="na">href=</span><span class="s">"/icon.svg"</span> <span class="na">type=</span><span class="s">"image/svg+xml"</span><span class="nt">&gt;</span>
  79. <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"apple-touch-icon"</span> <span class="na">href=</span><span class="s">"/apple-touch-icon.png"</span><span class="nt">&gt;</span><span class="c">&lt;!-- 180×180 --&gt;</span>
  80. <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"manifest"</span> <span class="na">href=</span><span class="s">"/manifest.webmanifest"</span><span class="nt">&gt;</span>
  81. </code></pre></div></div>
  82. <p>And in your web app manifest:</p>
  83. <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">//</span><span class="w"> </span><span class="err">manifest.webmanifest</span><span class="w">
  84. </span><span class="p">{</span><span class="w">
  85. </span><span class="nl">"icons"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
  86. </span><span class="p">{</span><span class="w"> </span><span class="nl">"src"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/icon-192.png"</span><span class="p">,</span><span class="w"> </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"image/png"</span><span class="p">,</span><span class="w"> </span><span class="nl">"sizes"</span><span class="p">:</span><span class="w"> </span><span class="s2">"192x192"</span><span class="w"> </span><span class="p">},</span><span class="w">
  87. </span><span class="p">{</span><span class="w"> </span><span class="nl">"src"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/icon-512.png"</span><span class="p">,</span><span class="w"> </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"image/png"</span><span class="p">,</span><span class="w"> </span><span class="nl">"sizes"</span><span class="p">:</span><span class="w"> </span><span class="s2">"512x512"</span><span class="w"> </span><span class="p">}</span><span class="w">
  88. </span><span class="p">]</span><span class="w">
  89. </span><span class="p">}</span><span class="w">
  90. </span></code></pre></div></div>
  91. <p>That is it. If you want to know how I arrived at this, which compromises I had to take, and how to build a set like this from scratch in a step-by-step fashion, tune in for the rest of the article.</p>
  92. <h2 id="long-version-where-everything-is-explained">Long version, where everything is explained</h2>
  93. <div class="post__note">
  94. <p>“Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.”</p>
  95. <p>—Antoine de Saint-Exupéry, Airman’s Odyssey</p>
  96. </div>
  97. <p>A concept of a favicon, short for “favorite icon”, have been around since the early 2000s. We all see those cute little images in your browser’s tab bar that help tell open websites apart every day. Users <em>expect</em> your website to have a favicon. It’s one of those little things that make other people take you seriously.</p>
  98. <p>Even Apple, which always had some aesthetic beef with icons that don’t come from Cupertino and downplayed favicons in Safari for years, had finally given up and now displays them properly across all of its devices.</p>
  99. <blockquote>
  100. <p>If you have a public-facing website, it has to have a favicon. Sadly, what users perceive as one icon—is actually <em>a lot</em> of them.</p>
  101. </blockquote>
  102. <p>So it is common to offload the grueling task of generating necessary files for the ever-growing list of screens and devices to favicon generator tools. No human in their right mind would want to spend hours creating them by hand. We are here to build websites, after all, not make browser vendors happy.</p>
  103. <figure class="post-media post-media--centered">
  104. <p><img class="post-media__object" alt="A set of favicons generated by a popular online generator" src="https://cdn.evilmartians.com/front/posts/how-to-favicon-in-2021-six-files-that-fit-most-needs/generated-e1f0bbd.png"></p>
  105. <figcaption class="post-media__caption">
  106. <p>A set of favicons generated by a popular online generator</p>
  107. </figcaption>
  108. </figure>
  109. <p>As a creator of <a href="https://github.com/ai/nanoid/">NanoID</a> and a proponent of minimalistic open source, I tend to think in a slightly different direction. What is the most efficient set of website icons? Which formats are outdated? Which icon types can be replaced with small compromises?</p>
  110. <p>So, I set out to create a minimal list of favicons that will work in all cases and in all browsers, barring some edge cases where it will still work, just not 100% perfectly.</p>
  111. <h2 id="the-ultimate-favicon-setup">The Ultimate Favicon Setup</h2>
  112. <p>Instead of creating many images with different sizes, I decided to rely on SVG and browser downscaling. If you are concerned about performance, I am here to set the record straight:</p>
  113. <ul>
  114. <li>browsers download favicons in the background, so a bigger favicon image does not affect website performance;</li>
  115. <li>SVG is a great way to reduce image size for images that are not supposed to be bitmaps in the first place; for many <em>logos</em>, the resulting file will be much smaller than a PNG;</li>
  116. <li>With just three PNG images in this minimum set, you can use advanced tools to optimize their size. It solves a problem for Internet users that don’t have unlimited data plans.</li>
  117. </ul>
  118. <p>Here comes the minimal set of icons that I came up with in my research and practice. It should work with all popular browsers and devices, old and new.</p>
  119. <h4 id="i-faviconico-for-legacy-browsers">I. favicon.ico for legacy browsers</h4>
  120. <p>ICO files actually have a <a href="https://en.wikipedia.org/wiki/ICO_(file_format)#Icon_resource_structure">directory structure</a> and can pack files with different resolutions. I recommend sticking to a single 32×32 image, unless the one you have doesn’t downscale well to 16×16 (becomes blurry, for instance). In that case, you can ask your designer to come up with a special version of the logo that is tailored to fit small pixel grids.</p>
  121. <p>Don’t get smart with folder static asset folder structure and cache busters. <code>https://example.com</code> website should have a favicon on <code>https://example.com/favicon.ico</code>. Some tools, like RSS readers, just request <code>/favicon.ico</code> from the server and don’t bother looking elsewhere.</p>
  122. <p>We need <code>size="any"</code> for <code>&lt;link&gt;</code> to <code>.ico</code> file to fix <a href="https://twitter.com/subzey/status/1417099064949235712">Chrome bug</a> of choosing ICO file over SVG.</p>
  123. <h4 id="ii-a-single-svg-icon-with-lightdark-version-for-modern-browsers">II. A single SVG icon with light/dark version for modern browsers</h4>
  124. <p>SVG is a vector format that describes curves instead of pixels. On large sizes, it is more efficient than raster images. <a href="https://caniuse.com/link-icon-svg">72% of all browsers</a> support SVG icons as of this writing.</p>
  125. <p>Your HTML page should have a <code>&lt;link&gt;</code> tag in its <code>&lt;head&gt;</code> with <code>rel="icon"</code>, <code>type="image/svg+xml"</code> and <code>href</code> with a link to SVG file as attributes.</p>
  126. <p>SVG is an XML format and can contain a <code>&lt;style&gt;</code> tag that describes CSS. As any CSS, it can contain media queries like <code>@media (prefers-color-scheme: dark)</code>. This will allow you to toggle the same icon between <a href="https://blog.tomayac.com/2019/09/21/prefers-color-scheme-in-svg-favicons-for-dark-mode-icons/">light and dark system themes</a>.</p>
  127. <h4 id="iii-180180-png-image-for-apple-devices">III. 180×180 PNG image for Apple devices</h4>
  128. <p>Apple touch icon is an image that Apple devices will use if you add the webpage as a shortcut to your home screen on an iPhone or iPad. Your HTML page should have <code>&lt;link rel="apple-touch-icon" href="apple-touch-icon.png"&gt;</code> tag in a <code>&lt;head&gt;</code>.</p>
  129. <p>iPads since iOS 8+ require a 180×180 resolution. Other devices will downscale the image, but if we provide the source with a good-enough quality, the downscaling won’t hurt the end-user (I’ll come back to it later).</p>
  130. <p>Small note: an Apple touch icon will look better if you add a <code>20px</code> padding around the icon and put some background color. You can use any image editor for that.</p>
  131. <h4 id="iv-web-app-manifest-with-192192-and-512512-png-icons-for-android-devices">IV. Web app manifest with 192×192 and 512×512 PNG icons for Android devices.</h4>
  132. <ul>
  133. <li>Web app manifest is a JSON file with all details for a browser to install your website as a system application. The format came from Google for its <a href="https://en.wikipedia.org/wiki/Progressive_web_application">PWA</a> initiative.</li>
  134. <li>Your HTML page should have a <code>&lt;link rel="manifest" href="path.webmanifest"&gt;</code> tag with a link to manifest file.</li>
  135. <li>Manifest should have an <code>icon</code> field that links to two icons: 192×192 displayed on a home screen and 512×512 that will be used as a splash screen as PWA is loading.</li>
  136. </ul>
  137. <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  138. </span><span class="nl">"icons"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
  139. </span><span class="p">{</span><span class="w"> </span><span class="nl">"src"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/icon-192.png"</span><span class="p">,</span><span class="w"> </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"image/png"</span><span class="p">,</span><span class="w"> </span><span class="nl">"sizes"</span><span class="p">:</span><span class="w"> </span><span class="s2">"192x192"</span><span class="w"> </span><span class="p">},</span><span class="w">
  140. </span><span class="p">{</span><span class="w"> </span><span class="nl">"src"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/icon-512.png"</span><span class="p">,</span><span class="w"> </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"image/png"</span><span class="p">,</span><span class="w"> </span><span class="nl">"sizes"</span><span class="p">:</span><span class="w"> </span><span class="s2">"512x512"</span><span class="w"> </span><span class="p">}</span><span class="w">
  141. </span><span class="p">]</span><span class="w">
  142. </span><span class="p">}</span><span class="w">
  143. </span></code></pre></div></div>
  144. <h2 id="did-we-forget-anyone">Did we forget anyone?</h2>
  145. <p>There are, of course, more favicon flavors out there, some of them quite obscure, so let’s see how our setup fares with them. Maybe, it’s time to say farewell to some less successful formats out there.</p>
  146. <h4 id="windows-tile-icon">Windows Tile Icon</h4>
  147. <p>Microsoft Edge used to <a href="https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/hh772707(v=vs.85)">support</a> a special icon format to pin websites to Start Menu. For recent versions of Windows, this is no longer required.</p>
  148. <h4 id="safari-pinned-icon">Safari Pinned Icon</h4>
  149. <p>Safari used to have a separate requirement to display an icon in <a href="https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariWebContent/pinnedTabs/pinnedTabs.html">pinned tabs</a>. However, since Safari 12, we can use a regular favicon for pinned tabs. Even <a href="https://www.apple.com/">apple.com</a> doesn’t use the <code>mask-icon</code> anymore.</p>
  150. <h4 id="relshortcut">rel=”shortcut”</h4>
  151. <p>A lot of (now outdated) tutorials will include a <code>favicon.ico</code> into HTML like this:</p>
  152. <pre><code>&lt;link rel="shortcut icon" href="/favicon.ico" sizes="any"&gt;
  153. </code></pre>
  154. <p>Be warned that <code>shortcut</code> is not, and never was a valid link relation. Read this <a href="https://mathiasbynens.be/notes/rel-shortcut-icon">amazing article</a> by Mathias Bynens from ten years ago that explains why we never needed shortcuts and <code>rel="icon"</code> is just fine.</p>
  155. <h4 id="yandex-tableau">Yandex Tableau</h4>
  156. <p><a href="https://browser.yandex.com/">Yandex Browser</a> is a Chromium-based browser from the biggest Russian search engine. In Russia, it has a 20% market share. It has <a href="https://yandex.com/support/browser-beta/personalization/tableau.html">a nice feature</a> that allows website to display live data in widgets on a home screen through a special JSON manifest provided by the <code>yandex-tableau-widget</code> link. However, the feature proved not to be very popular, and now Yandex has removed the technical documentation for it from its website. Regular icon manifests will work just as well.</p>
  157. <h4 id="opera-coast">Opera Coast</h4>
  158. <p>Opera Coast, an experimental browser for iOS, used to require a special 228×228 icon. The browser left the App Store in 2017, and I doubt it has survived multiple iOS updates since then.</p>
  159. <p>Now, as we waved good-bye to fallen comrades, let’s see how to produce an ultimate favicon set for those who are still standing.</p>
  160. <h2 id="how-to-build-our-ultimate-favicon-set">How to build our Ultimate Favicon Set</h2>
  161. <p>Here’s how to build our ultimate, minimalistic favicon set in six quick steps. All you need to start is an SVG file for the logo that you want to use.</p>
  162. <h4 id="step-1-prepare-the-svg">Step 1: Prepare the SVG</h4>
  163. <p>Be sure that the SVG image is <em>square</em>. Open the source file in your system viewer and check the image’s width and height. It is easy to adjust the SVG size in any SVG editor. In <a href="https://inkscape.org/">Inkscape</a>, you can change document size in File → Document Properties and center the logo using Object → Align and Distribute.</p>
  164. <p>Save your file as <code>icon.svg</code>. Now let’s fiddle with our SVG, so it plays well with modern system <em>themes</em>. Ask your designer how the colors should be inverted in a dark theme (for B&amp;W logos, you just change black to white).</p>
  165. <p>Now, open your SVG file in a <em>text</em> editor. Find a <code>&lt;path&gt;</code> with dark or missing <code>fill</code>. Add a CSS media query that triggers on theme changes and change <code>fill</code> to the colors you want:</p>
  166. <div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code> &lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500"&gt;
  167. <span class="gi">+ &lt;style&gt;
  168. + @media (prefers-color-scheme: dark) {
  169. + .a { fill: #f0f0f0 }
  170. + }
  171. + &lt;/style&gt;
  172. </span><span class="gd">- &lt;path fill="#0f0f0f" d="…" /&gt;
  173. </span><span class="gi">+ &lt;path class="a" fill="#0f0f0f" d="…" /&gt;
  174. </span> &lt;/svg&gt;
  175. </code></pre></div></div>
  176. <h4 id="step-2-create-an-ico-file">Step 2: Create an ICO file</h4>
  177. <p>Open your <code>icon.svg</code> file in a raster graphics editor. I recommend <a href="https://www.gimp.org/">GIMP</a>; it’s free and multi-platform.</p>
  178. <p>Accept rendering SVG to raster. Set width and height to 32 pixels. Export file to <code>favicon.ico</code> using <em>32 bpp, 8-bit alpha, no palette</em> settings.</p>
  179. <p>If you do not have GIMP you can install <a href="https://inkscape.org/">Inkscape</a> and <a href="https://www.imagemagick.org/script/download.php">ImageMagick</a> and convert SVG to ico in terminal:</p>
  180. <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>inkscape ./icon.svg <span class="nt">--export-width</span><span class="o">=</span>32 <span class="nt">--export-filename</span><span class="o">=</span><span class="s2">"./tmp.png"</span>
  181. <span class="c"># In Windows call `magick convert ./tmp.png ./favicon.ico`</span>
  182. convert ./tmp.png ./favicon.ico
  183. <span class="nb">rm</span> ./tmp.png
  184. </code></pre></div></div>
  185. <p>Scale the image down to 16×16 and check icon visibility. If it became too blurry, it would be better to ask your designer for a custom tiny version of the logo.</p>
  186. <p>To include a separate 16×16 version of an icon:</p>
  187. <ol>
  188. <li>Open <code>favicon.ico</code> with 32×32 icon.</li>
  189. <li>Create a new layer with 16×16 size.</li>
  190. <li>Put a 16×16 version of an icon into this layer.</li>
  191. <li>Export the file. GIMP will save each layout as a separate version of an icon.</li>
  192. </ol>
  193. <p>Or you can do the same in ImageMagick by:</p>
  194. <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>convert ./icon-32.png ./icon-16.png ./favicon.ico
  195. </code></pre></div></div>
  196. <h4 id="step-3-create-png-images">Step 3: Create PNG images</h4>
  197. <p>Open your source SVG file in a raster graphics editor again and create a 512×512 image. Export it as <code>icon-512.png</code>.<br>
  198. Scale the image to 192×192 and export it to <code>icon-192.png</code>. Now scale the image itself to 140×140 and set the canvas to 180×180, and export as <code>apple-touch-icon.png</code>.</p>
  199. <p>Or you can do the same in Inkscape by:</p>
  200. <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>inkscape ./icon.svg <span class="nt">--export-width</span><span class="o">=</span>512 <span class="nt">--export-filename</span><span class="o">=</span><span class="s2">"./icon-512.png"</span>
  201. inkscape ./icon.svg <span class="nt">--export-width</span><span class="o">=</span>192 <span class="nt">--export-filename</span><span class="o">=</span><span class="s2">"./icon-192.png"</span>
  202. </code></pre></div></div>
  203. <h4 id="step-4-optimize-png-and-svg-files">Step 4: Optimize PNG and SVG files</h4>
  204. <p>The best tool to optimize SVGs is <a href="https://github.com/svg/svgo">SVGO</a>. Run:</p>
  205. <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npx svgo <span class="nt">--multipass</span> icon.svg
  206. </code></pre></div></div>
  207. <p><a href="https://squoosh.app/">Squoosh</a> is a great web app to optimize raster images.</p>
  208. <ol>
  209. <li>Open your <code>icon-512.png</code> in Squoosh.</li>
  210. <li>Change Compress setting to <code>OxiPNG</code>.</li>
  211. <li>Enable “Reduce palette”.</li>
  212. <li>Set 64 colors.</li>
  213. <li>Compare before/after by moving a slider. If you see a difference, increase the number of colors.</li>
  214. <li>Save the file.</li>
  215. </ol>
  216. <p>Repeat these steps for <code>icon-192.png</code> and <code>apple-touch-icon.png</code>.</p>
  217. <h4 id="step-5-add-icons-to-html">Step 5: Add icons to HTML</h4>
  218. <p>You need to add links to <code>favicon.ico</code> and to <code>apple-touch-icon.png</code> into your HTML.</p>
  219. <p>For static HTML:</p>
  220. <div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code> &lt;title&gt;My website&lt;/title&gt;
  221. <span class="gi">+ &lt;link rel="icon" href="/favicon.ico" sizes="any"&gt;
  222. + &lt;link rel="icon" href="/icon.svg" type="image/svg+xml"&gt;
  223. + &lt;link rel="apple-touch-icon" href="/apple-touch-icon.png"&gt;
  224. </span></code></pre></div></div>
  225. <p>However, we recommend using a bundler to generate cache busters (include file’s hash in a name as a fingerprint). If you are using Webpack with [<code>html-webpack-plugin</code>]:</p>
  226. <ol>
  227. <li>
  228. <p>Create <code>index.html</code> template.</p>
  229. </li>
  230. <li>
  231. <p>Add template to plugin options:</p>
  232. <div class="language-js highlighter-rouge">
  233. <div class="highlight"><pre class="highlight"><code><span class="k">new</span> <span class="nx">HtmlWebpackPlugin</span><span class="p">({</span> <span class="na">template</span><span class="p">:</span> <span class="dl">"</span><span class="s2">./view/index.html</span><span class="dl">"</span> <span class="p">});</span>
  234. </code></pre></div> </div>
  235. </li>
  236. <li>
  237. <p>Define HTML template with links (examples use ERB used to include files, but can be your templating language of choice):</p>
  238. <div class="language-html highlighter-rouge">
  239. <div class="highlight"><pre class="highlight"><code><span class="cp">&lt;!DOCTYPE html&gt;</span>
  240. <span class="nt">&lt;html</span> <span class="na">lang=</span><span class="s">"en"</span><span class="nt">&gt;</span>
  241. <span class="nt">&lt;head&gt;</span>
  242. <span class="nt">&lt;meta</span> <span class="na">charset=</span><span class="s">"utf-8"</span><span class="nt">&gt;</span>
  243. <span class="nt">&lt;title&gt;</span>My website<span class="nt">&lt;/title&gt;</span>
  244. <span class="nt">&lt;meta</span> <span class="na">name=</span><span class="s">"viewport"</span> <span class="na">content=</span><span class="s">"width=device-width,initial-scale=1"</span><span class="nt">&gt;</span>
  245. <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"icon"</span> <span class="na">href=</span><span class="s">"/favicon.ico"</span> <span class="na">sizes=</span><span class="s">"any"</span><span class="nt">&gt;</span>
  246. <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"icon"</span> <span class="na">type=</span><span class="s">"image/svg+xml"</span> <span class="na">href=</span><span class="s">"&lt;%=
  247. require('./icon.svg').default
  248. %&gt;"</span><span class="nt">&gt;</span>
  249. <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"apple-touch-icon"</span> <span class="na">href=</span><span class="s">"&lt;%=
  250. require('./apple-touch-icon.png').default
  251. %&gt;"</span>
  252. <span class="nt">&gt;</span>
  253. <span class="nt">&lt;/head&gt;</span>
  254. <span class="nt">&lt;body&gt;&lt;/body&gt;</span>
  255. <span class="nt">&lt;/html&gt;</span>
  256. </code></pre></div> </div>
  257. </li>
  258. <li>
  259. <p>Use <a href="https://www.npmjs.com/package/copy-webpack-plugin"><code>copy-webpack-plugin</code></a> to copy <code>favicon.ico</code> <em>without</em> adding a hash to file name.</p>
  260. </li>
  261. </ol>
  262. <h4 id="free-tip-separate-icon-for-staging">Free Tip: Separate icon for staging</h4>
  263. <p>Favicons are a nice way to distinguish your production environment from a staging one. I find using an alternative icon for staging super effective in avoiding expensive confusions.</p>
  264. <p>Create a <code>favicon-dev.ico</code> with the same logo, but invert the colors (or do something else that makes sense for you). Same for SVG, create <code>icon-dev.svg</code>.</p>
  265. <p>Now, replace icons in your HTML template depending on <code>process.env.NODE_ENV === 'production'</code> condition:</p>
  266. <div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
  267. &lt;!doctype html&gt;
  268. &lt;html lang="en"&gt;
  269. &lt;head&gt;
  270. &lt;meta charset="utf-8"&gt;
  271. &lt;title&gt;My website&lt;/title&gt;
  272. &lt;meta name="viewport" content="width=device-width,initial-scale=1"&gt;
  273. <span class="gd">- &lt;link rel="icon" href="/favicon.ico" sizes="any"&gt;
  274. </span><span class="gi">+ &lt;link rel="icon" sizes="any" href="&lt;%=
  275. + process.env.NODE_ENV === 'production'
  276. + ? '/favicon.ico'
  277. + : require('./favicon-dev.ico').default
  278. + %&gt;"&gt;
  279. </span> &lt;link rel="icon" type="image/svg+xml" href="&lt;%=
  280. <span class="gd">- require('./icon.svg').default
  281. </span><span class="gi">+ process.env.NODE_ENV === 'production'
  282. + ? require('./icon.svg').default
  283. + : require('./icon-dev.svg').default
  284. </span> %&gt;"&gt;
  285. &lt;link rel="apple-touch-icon" href="&lt;%=
  286. require('./apple-touch-icon.png').default
  287. %&gt;"&gt;
  288. &lt;/head&gt;
  289. &lt;body&gt;&lt;/body&gt;
  290. &lt;/html&gt;
  291. </code></pre></div></div>
  292. <h4 id="step-6-create-a-web-app-manifest">Step 6: Create a web app manifest</h4>
  293. <p>For static HTML, create a JSON file named <code>manifest.webmanifest</code>:</p>
  294. <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  295. </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"My website"</span><span class="p">,</span><span class="w">
  296. </span><span class="nl">"icons"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
  297. </span><span class="p">{</span><span class="w"> </span><span class="nl">"src"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/icon-192.png"</span><span class="p">,</span><span class="w"> </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"image/png"</span><span class="p">,</span><span class="w"> </span><span class="nl">"sizes"</span><span class="p">:</span><span class="w"> </span><span class="s2">"192x192"</span><span class="w"> </span><span class="p">},</span><span class="w">
  298. </span><span class="p">{</span><span class="w"> </span><span class="nl">"src"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/icon-512.png"</span><span class="p">,</span><span class="w"> </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"image/png"</span><span class="p">,</span><span class="w"> </span><span class="nl">"sizes"</span><span class="p">:</span><span class="w"> </span><span class="s2">"512x512"</span><span class="w"> </span><span class="p">}</span><span class="w">
  299. </span><span class="p">]</span><span class="w">
  300. </span><span class="p">}</span><span class="w">
  301. </span></code></pre></div></div>
  302. <p>Link it in your HTML:</p>
  303. <div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code> &lt;title&gt;My website&lt;/title&gt;
  304. <span class="gi">+ &lt;link rel="manifest" href="/manifest.webmanifest"&gt;
  305. </span> &lt;link rel="icon" href="/favicon.ico" sizes="any"&gt;
  306. &lt;link rel="icon" href="/icon.svg" type="image/svg+xml"&gt;
  307. &lt;link rel="apple-touch-icon" href="/apple-touch-icon.png"&gt;
  308. </code></pre></div></div>
  309. <p>With Webpack, you can use <a href="https://www.npmjs.com/package/webpack-pwa-manifest">webpack-pwa-manifest</a> plugin:</p>
  310. <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nx">plugins</span><span class="p">:</span> <span class="p">[</span>
  311. <span class="err">…</span><span class="p">,</span>
  312. <span class="k">new</span> <span class="nx">WebpackPwaManifest</span><span class="p">({</span>
  313. <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">My website</span><span class="dl">'</span><span class="p">,</span>
  314. <span class="na">icons</span><span class="p">:</span> <span class="p">[</span>
  315. <span class="p">{</span> <span class="na">src</span><span class="p">:</span> <span class="nx">resolve</span><span class="p">(</span><span class="dl">'</span><span class="s1">./icon-192.png</span><span class="dl">'</span><span class="p">),</span> <span class="na">sizes</span><span class="p">:</span> <span class="dl">'</span><span class="s1">192x192</span><span class="dl">'</span> <span class="p">},</span>
  316. <span class="p">{</span> <span class="na">src</span><span class="p">:</span> <span class="nx">resolve</span><span class="p">(</span><span class="dl">'</span><span class="s1">./icon-512.png</span><span class="dl">'</span><span class="p">),</span> <span class="na">sizes</span><span class="p">:</span> <span class="dl">'</span><span class="s1">512x512 }
  317. ]
  318. })
  319. ]
  320. </span></code></pre></div></div>
  321. <hr>
  322. <p>Thank you for reading! As you can see, with modern web standards, the task of creating an ultimate favicon set becomes quite straightforward. And even though following the steps manually shouldn’t take much of your time, having an automated tool to do the same will be even more amazing! Feel free to ping me on <a href="https://twitter.com/sitnikcode">Twitter</a> if you’re willing to build one; I will be more than happy to help!</p>
  323. </article>
  324. <hr>
  325. <footer>
  326. <p>
  327. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  328. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
  329. </svg> Accueil</a> •
  330. <a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
  331. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-rss2"></use>
  332. </svg> Suivre</a> •
  333. <a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
  334. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-user-tie"></use>
  335. </svg> Pro</a> •
  336. <a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
  337. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-mail"></use>
  338. </svg> Email</a> •
  339. <abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
  340. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-hammer2"></use>
  341. </svg> Légal</abbr>
  342. </p>
  343. <template id="theme-selector">
  344. <form>
  345. <fieldset>
  346. <legend><svg class="icon icon-brightness-contrast">
  347. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-brightness-contrast"></use>
  348. </svg> Thème</legend>
  349. <label>
  350. <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
  351. </label>
  352. <label>
  353. <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
  354. </label>
  355. <label>
  356. <input type="radio" value="light" name="chosen-color-scheme"> Clair
  357. </label>
  358. </fieldset>
  359. </form>
  360. </template>
  361. </footer>
  362. <script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
  363. <script>
  364. function loadThemeForm(templateName) {
  365. const themeSelectorTemplate = document.querySelector(templateName)
  366. const form = themeSelectorTemplate.content.firstElementChild
  367. themeSelectorTemplate.replaceWith(form)
  368. form.addEventListener('change', (e) => {
  369. const chosenColorScheme = e.target.value
  370. localStorage.setItem('theme', chosenColorScheme)
  371. toggleTheme(chosenColorScheme)
  372. })
  373. const selectedTheme = localStorage.getItem('theme')
  374. if (selectedTheme && selectedTheme !== 'undefined') {
  375. form.querySelector(`[value="${selectedTheme}"]`).checked = true
  376. }
  377. }
  378. const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
  379. window.addEventListener('load', () => {
  380. let hasDarkRules = false
  381. for (const styleSheet of Array.from(document.styleSheets)) {
  382. let mediaRules = []
  383. for (const cssRule of styleSheet.cssRules) {
  384. if (cssRule.type !== CSSRule.MEDIA_RULE) {
  385. continue
  386. }
  387. // WARNING: Safari does not have/supports `conditionText`.
  388. if (cssRule.conditionText) {
  389. if (cssRule.conditionText !== prefersColorSchemeDark) {
  390. continue
  391. }
  392. } else {
  393. if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
  394. continue
  395. }
  396. }
  397. mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
  398. }
  399. // WARNING: do not try to insert a Rule to a styleSheet you are
  400. // currently iterating on, otherwise the browser will be stuck
  401. // in a infinite loop…
  402. for (const mediaRule of mediaRules) {
  403. styleSheet.insertRule(mediaRule.cssText)
  404. hasDarkRules = true
  405. }
  406. }
  407. if (hasDarkRules) {
  408. loadThemeForm('#theme-selector')
  409. }
  410. })
  411. </script>
  412. </body>
  413. </html>