<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>The notes of bnijenhuis</title>
  <link href="https://bnijenhuis.nl/feed.xml" rel="self"/>
  <link href="https://bnijenhuis.nl/"/>
  <updated>2025-02-02T00:00:00Z</updated>
  <id>https://bnijenhuis.nl/</id>
  <author>
    <name>Bernard Nijenhuis</name>
    <email>bernard@bnijenhuis.nl</email>
  </author>
  
  <entry>
    <title>Adding Prettier in Eleventy using Transforms</title>
    <link href="https://bnijenhuis.nl/notes/adding-prettier-in-eleventy-using-transforms/"/>
    <updated>2025-02-02T00:00:00Z</updated>
    <id>https://bnijenhuis.nl/notes/adding-prettier-in-eleventy-using-transforms/</id>
    <content type="html">&lt;p&gt;Recently I decided to look into some functionality in Eleventy I hadn&#39;t used before: &lt;a href=&quot;https://www.11ty.dev/docs/transforms/&quot;&gt;Transforms&lt;/a&gt;. According to their website:&lt;/p&gt;
&lt;p&gt;&lt;q cite=&quot;https://www.11ty.dev/docs/transforms/&quot;&gt;Transforms can modify a template’s output. For example, use a transform to format/prettify an HTML file with proper whitespace.&lt;/q&gt;&lt;/p&gt;
&lt;p&gt;So let&#39;s try and use this functionality to have nicely formatted HTML by using &lt;a href=&quot;https://prettier.io/&quot;&gt;Prettier&lt;/a&gt;. Prettier is a code formatter that can be used for a variety of languages and is customizable to your own preferences.&lt;/p&gt;
&lt;h2&gt;Added Prettier to the Eleventy config&lt;/h2&gt;
&lt;p&gt;Before using Prettier in Eleventy it needs to be installed. It&#39;s pretty (no pun intended) self-explanatory when visiting their docs: &lt;a href=&quot;https://prettier.io/docs/install/&quot;&gt;https://prettier.io/docs/install/&lt;/a&gt;. After installing you&#39;ll be able to use it in your Eleventy project. Go to your Eleventy config file and add the require statement for Prettier:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; prettier &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;prettier&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next up, add the transform to the &lt;code&gt;module.exports&lt;/code&gt; as follows:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addTransform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;prettier&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;outputPath &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;endsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;.html&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; prettified &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; prettier&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token literal-property property&quot;&gt;bracketSameLine&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token literal-property property&quot;&gt;printWidth&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;512&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token literal-property property&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;html&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token literal-property property&quot;&gt;tabWidth&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; prettified&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;// If not an HTML output, return content as-is&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; content&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code will add the &lt;code&gt;prettier&lt;/code&gt; transform, which handles all the content. It checks if the output path ends with &lt;code&gt;.html&lt;/code&gt;, because we only want to format the HTML pages. If it&#39;s a HTML page, format the content of the page with whatever &lt;a href=&quot;https://prettier.io/docs/options&quot;&gt;Prettier settings&lt;/a&gt; you prefer and return the pretty content. For all other content, don&#39;t do anything and return it as-is.&lt;/p&gt;
&lt;p&gt;The settings I use are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://prettier.io/docs/options#bracket-line&quot;&gt;bracketSameLine: true&lt;/a&gt;:
&lt;em&gt;Put the &amp;gt; of a multi-line HTML (HTML, JSX, Vue, Angular) element at the end of the last line instead of being alone on the next line (does not apply to self closing elements).&lt;/em&gt;
I find it much easier to read when the closing &amp;gt; is on the same line;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://prettier.io/docs/options#print-width&quot;&gt;printWidth: 512&lt;/a&gt;:
&lt;em&gt;Specify the line length that the printer will wrap on.&lt;/em&gt;
Although they recommend a much lower value than I use, I have some inline SVG&#39;s that are much larger. Breaking them up makes it harder to read the code, so I&#39;ve chosen to use a large value;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://prettier.io/docs/options#parser&quot;&gt;parser: &amp;quot;html&amp;quot;&lt;/a&gt;:
&lt;em&gt;Specify which parser to use.&lt;/em&gt;
Prettier has a lot of parsers, but because I&#39;m currently only using it to make the HTML pretty I use the &amp;quot;html&amp;quot; parser;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://prettier.io/docs/options#tab-width&quot;&gt;tabWidth: 2&lt;/a&gt;:
&lt;em&gt;Specify the number of spaces per indentation-level.&lt;/em&gt;
Again, this is just a setting I prefer. I switched between 2 and 4, but I find there&#39;s too much white space when using a tab width of 4 spaces. I realise it&#39;s set to the default now and I could leave it out, but I might just switch it again later on, so I just leave it in for now.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&#39;s all there is to it. Running your Eleventy build will now have ✨pretty✨ HTML pages!&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>User friendly dark mode</title>
    <link href="https://bnijenhuis.nl/notes/user-friendly-dark-mode/"/>
    <updated>2024-10-14T00:00:00Z</updated>
    <id>https://bnijenhuis.nl/notes/user-friendly-dark-mode/</id>
    <content type="html">&lt;p&gt;This website is implemented with a very minimal style, with black text on a white background. High contrast, easy to read, no distractions. But opening the website when you&#39;re in a darker environment is like accidentally turning on a flashlight right in your face. Let&#39;s do something about this.&lt;/p&gt;
&lt;h2&gt;Preferred color scheme&lt;/h2&gt;
&lt;p&gt;I&#39;m sure you&#39;ve heard of dark mode before, and a lot of websites have implemented functionality to switch between dark mode and light mode. But before we dive into that functionality, let&#39;s handle the system settings first. A user can set their preference for a dark or light mode in their operating system. This affects, among many other things, how the browser UI is shown. We can tap into this preference in CSS via the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme&quot;&gt;preferred color scheme&lt;/a&gt; media query. To do this we need to define some default colors as &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties&quot;&gt;CSS variables&lt;/a&gt; and override these colors with the media query. Most of the time developers use the light mode of the website as the default style and overwrite these values for users that prefer a dark color scheme. So for this website the implementation would be something like the following:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token property&quot;&gt;--text-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; black&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token property&quot;&gt;--bg-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; white&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;prefers-color-scheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dark&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token selector&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;--text-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; white&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;--bg-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; black&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token selector&quot;&gt;html&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--text-color&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--bg-color&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token property&quot;&gt;color-scheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; light dark&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&#39;ve added the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/color-scheme&quot;&gt;color-scheme property&lt;/a&gt; to the html selector as well, which indicates that the website supports both light and dark mode. This will change the default colors of interaction UI, form controls and some other things. So if there&#39;s no style provided for those specific elements, the color-scheme property will handle these nicely for light and dark mode.&lt;/p&gt;
&lt;h2&gt;Let the user choose&lt;/h2&gt;
&lt;p&gt;Even though we&#39;re respecting the system settings now, there&#39;s no way for the user to set the preference for this website alone. The user might have their system set to dark mode, but might prefer your website in light mode. Or the other way around.&lt;/p&gt;
&lt;p&gt;To provide this service to the user, we need to cater for 3 states:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;System preference;&lt;/li&gt;
&lt;li&gt;Dark mode;&lt;/li&gt;
&lt;li&gt;Light mode.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Determine the markup&lt;/h3&gt;
&lt;p&gt;The best implementation would be a tri-state button, but unfortunately there&#39;s no such thing. Using radio buttons would be a nice solution, but as Hidde de Vries mentioned in his &lt;a href=&quot;https://hidde.blog/dark-light/&quot;&gt;dark mode article&lt;/a&gt; that isn&#39;t really intuitive for keyboard users (and I agree). So what then?&lt;/p&gt;
&lt;p&gt;There are a lot of different implementations, and all of them have their own pros and cons, but I&#39;m going for a button implementation. I don&#39;t want 1 toggle button, because I want the 3 options mentioned above and not just a toggle between dark and light mode. Thse buttons need to be grouped together, so to do this we need to add a &lt;code&gt;fieldset&lt;/code&gt; around them with a &lt;code&gt;legend&lt;/code&gt; to label the group. Combined with the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-pressed&quot;&gt;&lt;code&gt;aria-pressed&lt;/code&gt; attribute&lt;/a&gt; to indicate which button is selected, we get something like this:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;themes&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;hidden&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;fieldset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;legend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Themes&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;legend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;aria-pressed&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;false&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-theme&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;system&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;System preference&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;aria-pressed&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;false&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-theme&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;light&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Light mode&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;aria-pressed&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;false&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-theme&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;dark&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Dark mode&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;fieldset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now because this functionality will only work with JavaScript, we have to show this only when JavaScript is enabled. That&#39;s why I&#39;ve added a &lt;code&gt;div&lt;/code&gt; with the &lt;code&gt;hidden&lt;/code&gt; attribute so it&#39;s hidden by default, and using JavaScript we&#39;re going to remove that &lt;code&gt;hidden&lt;/code&gt; attribute. Another way would be to add all the HTML via JavaScript, but I find this way a bit more maintainable and readable.&lt;/p&gt;
&lt;h3&gt;Adding the functionality&lt;/h3&gt;
&lt;p&gt;There will be a few steps that need to be done to complete the functionality:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Show markup of the functionality;&lt;/li&gt;
&lt;li&gt;Handle selected theme.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Show markup of the functionality&lt;/h4&gt;
&lt;p&gt;As I&#39;ve mentioned before, I&#39;ve hidden the markup by default, so we need to show this first by removing the &lt;code&gt;hidden&lt;/code&gt; attribute:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;.themes&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;hidden&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Handle selected theme&lt;/h4&gt;
&lt;p&gt;Now that the markup is visible we can handle the buttons. We&#39;re going to save the selected theme in &lt;code&gt;localStorage&lt;/code&gt;, make sure the clicked button is selected while the other buttons are not and add a class to the &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; for the selected theme while removing any other themes from the &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; classes. The code for all of this will be something like this (with comments to explain):&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// get the selected theme from localStorage and fallback to system&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; theme &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;localStorage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;theme&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; localStorage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;theme&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;system&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// set selected button&lt;/span&gt;&lt;br&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;.themes button[data-theme=&quot;&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; theme &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&quot;]&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;aria-pressed&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;true&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// get theme buttons&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; themeButtons &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;.themes button&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// handle click on button&lt;/span&gt;&lt;br&gt;themeButtons&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;themeButton&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    themeButton&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;click&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// get selected theme&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; selectedTheme &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; themeButton&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;data-theme&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// save selected theme to localStorage&lt;/span&gt;&lt;br&gt;        localStorage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;theme&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; selectedTheme&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// add pressed state to button&lt;/span&gt;&lt;br&gt;        themeButton&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;aria-pressed&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;true&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// add class to html so we can handle theme in css&lt;/span&gt;&lt;br&gt;        document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;html&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;theme-&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; selectedTheme&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// make sure other buttons and themes aren&#39;t selected&lt;/span&gt;&lt;br&gt;        themeButtons&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;otherThemeButton&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;            &lt;span class=&quot;token comment&quot;&gt;// button does not match selected button&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;themeButton &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; otherThemeButton&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            &lt;br&gt;                &lt;span class=&quot;token comment&quot;&gt;// remove pressed state of button&lt;/span&gt;&lt;br&gt;                otherThemeButton&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;aria-pressed&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;false&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;                &lt;span class=&quot;token comment&quot;&gt;// remove possible themes from html&lt;/span&gt;&lt;br&gt;                document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;html&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;theme-&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; otherThemeButton&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;data-theme&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;                &lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What&#39;s left is to handle the selected theme on pageload. We&#39;ve already checked &lt;code&gt;localStorage&lt;/code&gt; for the saved theme in the code we just wrote, and we already add the theme class to the &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; when we click one of the buttons, but we&#39;re not setting the right theme on pageload yet. So we need these two lines of code we wrote earlier:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; theme &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;localStorage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;theme&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; localStorage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;theme&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;system&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;html&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;theme-&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; theme&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make sure to place these two lines in a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; block in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;, right before loading the stylesheet to prevent flickering. Because if you do it later on, the style is loaded first, you then change the theme, which might cause a change from light to dark mode (or the other way around).&lt;/p&gt;
&lt;h2&gt;Don&#39;t forget the favicon&lt;/h2&gt;
&lt;p&gt;If the user has their operating system preferences set to dark mode, the browser UI will be dark. If your favicon happens to be dark as well, it could be that it&#39;s not recognizable anymore (which is the whole point of the favicon). There is a solution for that (well, for most browsers, that is): &lt;a href=&quot;https://caniuse.com/link-icon-svg&quot;&gt;SVG favicons&lt;/a&gt;. Because SVG favicons can contain CSS and, more importantly, media queries, which allows us to do something similar to what we did earlier to handle the operating system preference in our website. Together with the code of the SVG, we get something like this:&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;svg&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;viewBox&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0 0 128 128&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;xmlns&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;http://www.w3.org/2000/svg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style&quot;&gt;&lt;span class=&quot;token language-css&quot;&gt;&lt;br&gt;		&lt;span class=&quot;token selector&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #000000&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;    	&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;prefers-color-scheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dark&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;			&lt;span class=&quot;token selector&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #ffffff&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;    	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;	&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;path&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;M5.714 44.034c-.99-.849-.99-3.82.283-8.772 1.274-4.951 2.547-8.347 4.103-9.903.99-.99 3.537-1.556 7.782-1.556 7.498 0 12.591.848 15.42 2.405.991.707 1.557 3.537 1.557 8.347 0 1.415 0 3.254-.141 5.66-.142 2.404-.142 4.244-.142 5.517 0 1.273.142 2.122.283 2.405.283.283.99.142 2.264-.424 1.132-.566 2.83-1.274 4.952-1.981 2.122-.707 4.385-.99 6.65-.707 4.95.565 8.77 3.395 11.459 8.488 2.547 5.094 3.82 11.743 3.82 19.808 0 2.688-.141 4.81-.283 6.225-.707 6.932-2.688 12.591-5.942 16.977-3.396 4.386-7.64 6.791-12.875 7.499l-3.254.141c-2.264 0-4.386-.283-6.508-.707-2.122-.566-3.82-1.132-5.376-1.84-1.698-.707-2.405-.99-2.547-.99-.99-.424-1.98-.707-2.97-.566-.991.142-1.84.708-2.548 1.981-.424.99-2.546 1.132-6.083.283-3.679-.707-5.8-1.556-6.225-2.688-.142-.283-.283-1.556-.283-3.962 0-3.112.141-8.488.566-16.27.283-7.781.566-14.572.707-20.656.142-5.942.142-9.196 0-9.904-.141-.565-.283-1.131-.707-1.697l-1.132-1.132c-.283-.283-.85-.708-1.556-1.132-.708-.424-1.132-.707-1.274-.849zm27.589 37.21c2.405.424 4.244.14 6.225-.85 1.415-.99 2.83-2.263 3.678-4.102 1.415-3.679.566-6.367-2.83-7.923-.848-.425-1.838-.566-2.829-.142-1.415.425-2.263 2.688-3.254 6.65-1.415 3.961-1.415 6.083-.99 6.366z&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;path&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;M61.174 84.356c.708-.707 1.698-1.273 3.254-1.698 1.415-.424 2.264-1.132 2.406-1.839.141-.849.424-1.84.707-3.113.141-1.131.283-1.98.424-2.688.142-.566.283-1.273.425-2.122.141-.707.141-1.415.283-1.84v-1.414c-.142-.424-.142-.849-.283-1.132-.142-.283-.283-.566-.566-.707-.566-.425-1.415-.85-2.405-1.274-.99-.424-1.84-.848-2.264-1.273-.424-.283-.849-.849-1.273-1.698-.425-.707-.566-1.697-.566-2.97 0-2.83.566-6.084 1.556-9.621.99-3.537 2.264-5.801 3.679-6.791 1.131-.708 3.961-1.132 8.63-1.132 7.781 0 12.309.707 13.299 1.98.566.85.707 3.113.424 6.65-.141 1.556.142 2.405.708 2.264.566-.142 1.273-.708 2.264-1.98.99-1.274 2.263-2.548 4.102-3.962 1.84-1.415 3.82-2.406 6.226-2.971 1.697-.425 2.97-.566 3.82-.566 3.537 0 6.79 1.273 9.479 3.82 2.688 2.546 4.244 6.083 4.669 10.47a32.94 32.94 0 01-.566 9.761c-.708 3.537-1.415 6.225-1.981 8.348-.566 2.122-.566 3.678-.142 4.527.283.424 1.132 1.132 2.406 1.98 1.273.85 2.263 1.698 2.688 2.689.707 1.273.424 3.82-.566 7.64-1.132 3.961-2.405 6.65-3.679 7.923-1.273 1.273-4.951 1.98-11.035 1.98-8.206 0-10.611-.849-11.177-2.546-.424-1.415-.566-5.235-.566-11.319 0-2.405 0-5.8.142-10.045v-7.781c0-.99-2.264-1.274-2.83-.85-.707.425-1.273 1.699-1.556 3.538-.142 2.122-.283 4.81-.142 8.064.142 3.254 0 6.933-.141 10.611-.142 3.82-.566 6.367-1.274 7.357-1.131 2.122-5.942 3.113-14.29 3.113-5.517 0-9.054-.566-10.327-1.84-1.273-1.131-2.547-4.102-3.82-8.913-1.132-4.669-1.132-7.64-.142-8.63z&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;svg&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will set the &lt;code&gt;fill&lt;/code&gt; of the &lt;code&gt;path&lt;/code&gt; to black, but if the user prefers a dark color scheme (and thus has a dark browser UI) it changes the &lt;code&gt;fill&lt;/code&gt; of the &lt;code&gt;path&lt;/code&gt; to white.&lt;/p&gt;
&lt;h2&gt;Finetuning contrast colors&lt;/h2&gt;
&lt;p&gt;Lastly I&#39;ve changed the color codes for black and white on this website to be a little easier on the eyes. I&#39;m not a designer (no shit), but &lt;a href=&quot;https://graphicdesign.stackexchange.com/a/25360&quot;&gt;this comment on StackExchange&lt;/a&gt; sums it up pretty well with multiple references (if you&#39;re interested). In short it states that although high contrast is good, too high of a contrast could be straining on the eyes. So for dark mode I&#39;ve changed the black to &lt;code&gt;#1f2020&lt;/code&gt; and the white to &lt;code&gt;#e2e2e2&lt;/code&gt; and for light mode I&#39;ve changed the black to &lt;code&gt;#1f2020&lt;/code&gt; and the white to &lt;code&gt;#f8f7f3&lt;/code&gt;, which makes a bit easier on the eyes.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Updating Eleventy...and more</title>
    <link href="https://bnijenhuis.nl/notes/updating-eleventy-and-more/"/>
    <updated>2024-09-11T00:00:00Z</updated>
    <id>https://bnijenhuis.nl/notes/updating-eleventy-and-more/</id>
    <content type="html">&lt;p&gt;As you might have noticed, I don&#39;t have a very stable streak in posting notes on here. It &lt;em&gt;only&lt;/em&gt; took almost a year between my last note and the one before that, so my setup tends to get outdated. And because I take so long, I keep forgetting what and how I need to update everything. So to make it easier for my future self I decided to document it.&lt;/p&gt;
&lt;h2&gt;Update &lt;code&gt;npm&lt;/code&gt; first&lt;/h2&gt;
&lt;p&gt;Last time I already received a notice in terminal that my &lt;a href=&quot;https://www.npmjs.com/&quot;&gt;npm&lt;/a&gt; version has a new major version available, so let&#39;s start with updating this. The notice already gave the command to update npm, in this case it was:&lt;/p&gt;
&lt;pre class=&quot;language-terminal&quot;&gt;&lt;code class=&quot;language-terminal&quot;&gt;npm install -g npm@10.8.3 &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And as usual, this returned a bunch of permission errors, because I forgot I need to install this as the &lt;code&gt;root&lt;/code&gt; user, so let&#39;s try again:&lt;/p&gt;
&lt;pre class=&quot;language-terminal&quot;&gt;&lt;code class=&quot;language-terminal&quot;&gt;sudo npm install -g npm@10.8.3 &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That command solved the permissions errors, but returned a new error:&lt;/p&gt;
&lt;pre class=&quot;language-terminal&quot;&gt;&lt;code class=&quot;language-terminal&quot;&gt;npm WARN EBADENGINE Unsupported engine {&lt;br&gt;npm WARN EBADENGINE   package: &#39;npm@10.8.3&#39;,&lt;br&gt;npm WARN EBADENGINE   required: { node: &#39;^18.17.0 || &gt;=20.5.0&#39; },&lt;br&gt;npm WARN EBADENGINE   current: { node: &#39;v13.7.0&#39;, npm: &#39;7.8.0&#39; }&lt;br&gt;npm WARN EBADENGINE }&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;No wait, update &lt;code&gt;Node.js&lt;/code&gt; first&lt;/h2&gt;
&lt;p&gt;My &lt;a href=&quot;https://nodejs.org/en&quot;&gt;Node.js&lt;/a&gt; version appears to be in need of an update. There are several ways of doing this, so I just go for the easiest one, which is downloading and installing it. After restarting the terminal to make sure the changes to take effect I double check the installation by seeing which Node.js version is installed now:&lt;/p&gt;
&lt;pre class=&quot;language-terminal&quot;&gt;&lt;code class=&quot;language-terminal&quot;&gt;node -v &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This returned &lt;code&gt;v20.17.0&lt;/code&gt;, so Node.js is now up to date.&lt;/p&gt;
&lt;h2&gt;NOW update &lt;code&gt;npm&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Node.js is updated to the right version so updating &lt;code&gt;npm&lt;/code&gt; should work now, so let&#39;s try again:&lt;/p&gt;
&lt;pre class=&quot;language-terminal&quot;&gt;&lt;code class=&quot;language-terminal&quot;&gt;sudo npm install -g npm@10.8.3 &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No errors return, so could it be that &lt;code&gt;npm&lt;/code&gt; is actually updated? Let&#39;s check:&lt;/p&gt;
&lt;pre class=&quot;language-terminal&quot;&gt;&lt;code class=&quot;language-terminal&quot;&gt;npm -v &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Success! The installed version of &lt;code&gt;npm&lt;/code&gt; is now &lt;code&gt;10.8.3&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Next up: update Eleventy&lt;/h2&gt;
&lt;p&gt;On to the actual purpose of this exercise: updating Eleventy. First check which version is installed:&lt;/p&gt;
&lt;pre class=&quot;language-terminal&quot;&gt;&lt;code class=&quot;language-terminal&quot;&gt;npx @11ty/eleventy --version &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Turns out I&#39;m running version 1.0.1, which is pretty old, because Eleventy is currently on version 2.0.1 (that is the latest stable version, but 3.0.0 is already in beta at the time of writing). There&#39;s some excellent &lt;a href=&quot;https://www.11ty.dev/docs/plugins/upgrade-help/&quot;&gt;documentation&lt;/a&gt; on their website on how to do this and there&#39;s even an upgrade helper to check your project after the upgrade. Nothing left to do than to follow these steps.&lt;/p&gt;
&lt;p&gt;Since I use as little dependencies as possible in my setup, I didn&#39;t run into any problems when upgrading Eleventy from v1 to v2. That means I&#39;m done and Eleventy is now updated to 2.0.1!&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Minimize CLS caused by font loading</title>
    <link href="https://bnijenhuis.nl/notes/minimize-cls-caused-by-font-loading/"/>
    <updated>2024-09-05T00:00:00Z</updated>
    <id>https://bnijenhuis.nl/notes/minimize-cls-caused-by-font-loading/</id>
    <content type="html">&lt;p&gt;As you might have noticed, the style of this website is pretty minimal. And because it&#39;s so minimal there&#39;s not much that could hurt the lighthouse score...or so you&#39;d think. Turns out that the one thing I did do already hurts the score: loading a web font.&lt;/p&gt;
&lt;p&gt;Every once in a while I check the scores of my website to see if things are still working correctly, and I noticed that on mobile the performance score dropped to 97. Although still good, it should be 100 for such a simple layout. The cause of it was the &lt;a href=&quot;https://web.dev/articles/cls&quot;&gt;Cumulative Layout Shift&lt;/a&gt; (or CLS). The difference between the webfont (Asap) and the fallback font (Arial) caused the layout to shift a bit while loading. In particular the character widths that are different enough to cause the line breaks to be at different places in the paragraphs.&lt;/p&gt;
&lt;p&gt;So it&#39;s clear what the problem is, but how can we solve it? Well, by creating a new fallback font that has matching character widths. The key however is to use a web-safe font. A web-safe font is a font that&#39;s already assumed to be on the majority of users&#39; devices, so there&#39;s no need to download the font. The fallback font is nothing more than a tweaked web-safe font. There are several tools available to do this, like &lt;a href=&quot;https://screenspan.net/fallback&quot;&gt;Fallback Font Generator&lt;/a&gt;, but i prefer to use &lt;a href=&quot;https://deploy-preview-15--upbeat-shirley-608546.netlify.app/perfect-ish-font-fallback/&quot;&gt;Automatic font matching&lt;/a&gt;. You just choose your font (make sure to have it installed on your system) and it will automatically generate a fallback font that matches your font as best as possible.&lt;/p&gt;
&lt;p&gt;What&#39;s left to do is to add the fallback font to your stylesheet by copying the code given to you, in this case:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@font-face&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Asap-fallback&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token property&quot;&gt;size-adjust&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 98.3%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token property&quot;&gt;ascent-override&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 91%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Arial&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and add it as a fallback:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Asap&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Asap-fallback&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Arial&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sans-serif&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To top it all off I added my webfont as a preload as well by adding it to the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; like this:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preload&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/fonts/Asap-VariableFont_wght.woff2&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;font&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;font/woff2&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;crossorigin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For more information about this I suggest you read &lt;a href=&quot;https://web.dev/articles/codelab-preload-web-fonts&quot;&gt;this short article&lt;/a&gt; that goes into the different attributes that are used.&lt;/p&gt;
&lt;p&gt;By making these two changes I&#39;ve improved the performance score in lighthouse back to a nice 100...until they decide to change the metrics again.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Add verified links to your Mastodon profile</title>
    <link href="https://bnijenhuis.nl/notes/add-verified-links-to-your-mastodon-profile/"/>
    <updated>2022-11-21T00:00:00Z</updated>
    <id>https://bnijenhuis.nl/notes/add-verified-links-to-your-mastodon-profile/</id>
    <content type="html">&lt;p&gt;Recently I, among a whole lot of others, revived &lt;a href=&quot;https://mastodon.social/@bnijenhuis&quot;&gt;my Mastodon account&lt;/a&gt; now that a spoiled brat took over Twitter and is currently burning it to the ground. Along with the revival I started searching for the people I&#39;d like to follow and I saw several accounts that had verified links on their profile. Now with Twitter it would be a real hassle to get a verified account (and it got even weirder with paid blue ticks, official ticks, etc.). On Mastodon you don&#39;t have verified accounts, but as it turned out you can add verified links to your profile.&lt;/p&gt;
&lt;p&gt;On Mastodon you get 4 fields to add meta data to your profile. You can use it for whatever you&#39;d like, but most people use at least one to add their website. Mastodon even added a block on how you can verify yourself as the owner of the link. All you have to do is add a reference to your Mastodon profile including a &lt;code&gt;rel=&amp;quot;me&amp;quot;&lt;/code&gt; attribute. This can be a link (like I&#39;ve used on my website), but it can also be a &lt;code&gt;meta&lt;/code&gt; tag in the &lt;code&gt;head&lt;/code&gt; of the website, as long it includes the &lt;code&gt;rel=&amp;quot;me&amp;quot;&lt;/code&gt; attribute.&lt;/p&gt;
&lt;p&gt;But there is a little catch, and (luckily for me) &lt;a href=&quot;https://mastodon.social/@paulvanbuuren/109358870755309347&quot;&gt;Paul had the answer&lt;/a&gt;&lt;sup&gt;[1]&lt;/sup&gt;. Do not, I repeat, do not add a trailing slash to your website. For whatever reason it won&#39;t verify the link if you add the website to your profile with a trailing slash.&lt;/p&gt;
&lt;p&gt;That&#39;s it. I couldn&#39;t figure this out myself, and I couldn&#39;t find any documentation about it. But apparently it works.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;sup&gt;[1]&lt;/sup&gt; Dutch toot where Paul suggests to remove the trailing slash.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Changing urls but keeping webmentions in Eleventy</title>
    <link href="https://bnijenhuis.nl/notes/changing-urls-but-keeping-webmentions-in-eleventy/"/>
    <updated>2022-11-03T00:00:00Z</updated>
    <id>https://bnijenhuis.nl/notes/changing-urls-but-keeping-webmentions-in-eleventy/</id>
    <content type="html">&lt;p&gt;I started out creating these notes with the date in the url, because I thought it was an easy way to keep some overview of my notes. But it turned out to be rather annoying when I was creating new posts. I rarely start and finish a post on the same day, which meant I would have to change the filename multiple times. It also meant that every url was 10 characters longer than it needed to be. So I&#39;ve decided to change it, because the date will always be in the post itself anyway. Also, &lt;a href=&quot;https://hidde.blog/new-uris/&quot;&gt;Hidde did it&lt;/a&gt;, so it must be the right thing to do ;).&lt;/p&gt;
&lt;p&gt;The thing is, I&#39;ve setup webmentions to add some interaction to my notes (as you can read in my note &lt;a href=&quot;https://bnijenhuis.nl/notes/implementing-clientside-webmentions/&quot;&gt;Implementing clientside webmentions&lt;/a&gt;). And some of my notes got some decent traction, so I&#39;d like to keep the webmentions I&#39;ve had thus far, but changing the urls would mean I would lose them. The solution to this was even simpler than I thought when I was looking at the &lt;a href=&quot;https://github.com/aaronpk/webmention.io#find-links-to-multiple-pages&quot;&gt;webmentions API&lt;/a&gt;. Turns out you can request the webmentions for multiple targets at once.&lt;/p&gt;
&lt;p&gt;For example, to get the webmentions of my first note I&#39;m calling this url:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;https://webmention.io/api/mentions.jf2?target=https://bnijenhuis.nl/notes/2021-03-01-unhurried-development/&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The new url (without the date) would be &lt;code&gt;https://bnijenhuis.nl/notes/unhurried-development/&lt;/code&gt;, so we need to add this url as a &lt;code&gt;target&lt;/code&gt; while getting the webmentions (and turn the &lt;code&gt;target&lt;/code&gt; parameter into an array), which would result in:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;https://webmention.io/api/mentions.jf2?target[]=https://bnijenhuis.nl/notes/2021-03-01-unhurried-development/&amp;amp;target[]=https://bnijenhuis.nl/notes/unhurried-development/&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;But how do we get the old url?&lt;/h2&gt;
&lt;p&gt;This too was pretty simple to do in Eleventy. You can define your own frontmatter variables, which means I could define the old url here (I named it &lt;code&gt;aliasUrl&lt;/code&gt;). For example, the frontmatter of my first note would become:&lt;/p&gt;
&lt;pre class=&quot;language-markup&quot;&gt;&lt;code class=&quot;language-markup&quot;&gt;---&lt;br&gt;pageTitle: &#39;Unhurried Development&#39;&lt;br&gt;date: 2021-03-01&lt;br&gt;aliasUrl: &#39;/notes/2021-03-01-unhurried-development/&#39;&lt;br&gt;tags:&lt;br&gt; - notes&lt;br&gt;---&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The webmentions functionality can access this variable and therefor we can add it as a &lt;code&gt;target&lt;/code&gt;. And that&#39;s all there is to it. Add a variable to the frontmatter and use this while getting the webmentions.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Setting up a local development environment</title>
    <link href="https://bnijenhuis.nl/notes/setting-up-a-local-development-environment/"/>
    <updated>2022-10-17T00:00:00Z</updated>
    <id>https://bnijenhuis.nl/notes/setting-up-a-local-development-environment/</id>
    <content type="html">&lt;p&gt;&lt;em&gt;Full disclosure: I&#39;m writing this mainly because I keep having to look up how I&#39;ve set up my development environment. I&#39;m happy with how it&#39;s set up, but because I use it for some hobby projects and the occasional client work, I keep forgetting how I have to set up a new project. Time to document it for my own reference (in case the reference articles disappear for some reason) and while I&#39;m at it I might as well share it with the rest of you.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;There are two detailed posts by &lt;a href=&quot;https://twitter.com/taniarascia&quot;&gt;Tania Rascia&lt;/a&gt; that I&#39;ve used for my setup:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.taniarascia.com/local-environment/&quot;&gt;https://www.taniarascia.com/local-environment/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.taniarascia.com/setting-up-virtual-hosts/&quot;&gt;https://www.taniarascia.com/setting-up-virtual-hosts/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I recommend you read these articles for more background information and/or how to set up the development environment on Windows. I&#39;ll go over the parts I&#39;ve used to set it up on my Mac. Again, to set it up on Windows, check out the articles of Tania.&lt;/p&gt;
&lt;h2&gt;Local server environment&lt;/h2&gt;
&lt;p&gt;There are different ways to set up a local server environment, but I find the easiest way to do this is by using &lt;a href=&quot;https://www.mamp.info/&quot;&gt;MAMP&lt;/a&gt;. This allows you to run Apache, MySQL and PHP with ease. You can even choose if you want to run Apache or Nginx, and you can choose which PHP version you want to run on your local server. Install it, open it, click on Start and you&#39;re good to go. If you want to run one project on &lt;code&gt;http://localhost:8888&lt;/code&gt; that is.&lt;/p&gt;
&lt;h2&gt;Virtual hosts&lt;/h2&gt;
&lt;p&gt;If you only want to run one project at once on &lt;code&gt;localhost&lt;/code&gt;, you can stick with the setup MAMP has provided. You only have to change the &lt;code&gt;Document root&lt;/code&gt; to your project and restart the server. I, however, prefer to run multiple projects on their own urls. Here&#39;s what you have to do to achieve this.&lt;/p&gt;
&lt;h3&gt;Enable virtual hosts in Apache&lt;/h3&gt;
&lt;p&gt;You only need to do this once. Open the apache config file located at &lt;code&gt;/Applications/MAMP/conf/apache/httpd.conf&lt;/code&gt; and enable the following line (it&#39;s hashed out by default):&lt;/p&gt;
&lt;pre class=&quot;language-apacheconf&quot;&gt;&lt;code class=&quot;language-apacheconf&quot;&gt;&lt;span class=&quot;token directive-inline property&quot;&gt;Include&lt;/span&gt; /Applications/MAMP/conf/apache/extra/httpd-vhosts.conf&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Allow symlink override&lt;/h3&gt;
&lt;p&gt;This too has to be done only once. In the same apache config file, located at &lt;code&gt;/Applications/MAMP/conf/apache/httpd.conf&lt;/code&gt;, change this:&lt;/p&gt;
&lt;pre class=&quot;language-apacheconf&quot;&gt;&lt;code class=&quot;language-apacheconf&quot;&gt;&lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Directory&lt;/span&gt;&lt;span class=&quot;token directive-block-parameter attr-value&quot;&gt; /&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token directive-inline property&quot;&gt;Options&lt;/span&gt; Indexes FollowSymLinks&lt;br&gt;    &lt;span class=&quot;token directive-inline property&quot;&gt;AllowOverride&lt;/span&gt; None&lt;br&gt;&lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;Directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;to this:&lt;/p&gt;
&lt;pre class=&quot;language-apacheconf&quot;&gt;&lt;code class=&quot;language-apacheconf&quot;&gt;&lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Directory&lt;/span&gt;&lt;span class=&quot;token directive-block-parameter attr-value&quot;&gt; /&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token directive-inline property&quot;&gt;Options&lt;/span&gt; Indexes FollowSymLinks&lt;br&gt;  &lt;span class=&quot;token directive-inline property&quot;&gt;AllowOverride&lt;/span&gt; All&lt;br&gt;&lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;Directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Add virtual host config&lt;/h3&gt;
&lt;p&gt;Next up, you need to add the actual virtual host config. You need to do this for every project you want to run on a different url. Open the Apache vhost config file, located at &lt;code&gt;/Applications/MAMP/conf/apache/extra/httpd-vhosts.conf&lt;/code&gt;. At the end of the file add the following for each project:&lt;/p&gt;
&lt;pre class=&quot;language-apacheconf&quot;&gt;&lt;code class=&quot;language-apacheconf&quot;&gt;&lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;VirtualHost&lt;/span&gt;&lt;span class=&quot;token directive-block-parameter attr-value&quot;&gt; *&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;80&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token directive-inline property&quot;&gt;ServerName&lt;/span&gt; myproject.test&lt;br&gt;  &lt;span class=&quot;token directive-inline property&quot;&gt;DocumentRoot&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/Users/bnijenhuis/Sites/myproject&quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;VirtualHost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Enter the url you want to use for &lt;code&gt;ServerName&lt;/code&gt; (do not use &lt;code&gt;.dev&lt;/code&gt; however, because that&#39;s not supported anymore), and enter the location of the project for &lt;code&gt;DocumentRoot&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Add project url to the &lt;code&gt;hosts&lt;/code&gt; file&lt;/h2&gt;
&lt;p&gt;MAMP will now serve the projects at the urls you&#39;ve provided in the virtual host config, but the browser won&#39;t recognize these urls yet. You have to add the project urls to the &lt;code&gt;hosts&lt;/code&gt; file. Open your hosts file, located at &lt;code&gt;/etc/hosts&lt;/code&gt;, and add your projects at the end of the file (each project on a new line) like this:&lt;/p&gt;
&lt;pre class=&quot;language-apacheconf&quot;&gt;&lt;code class=&quot;language-apacheconf&quot;&gt;127.0.0.1   myproject.test&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Remove the port from the url&lt;/h2&gt;
&lt;p&gt;We&#39;re almost there. The project is on the right url, served from the right folder, but it has a port number in the url. In this example it&#39;s &lt;code&gt;myproject.test:8888&lt;/code&gt;, so we have to change that (this also has to be done once). Open the apache config file again, located at &lt;code&gt;/Applications/MAMP/conf/apache/httpd.conf&lt;/code&gt;, and change the following lines:&lt;/p&gt;
&lt;pre class=&quot;language-apacheconf&quot;&gt;&lt;code class=&quot;language-apacheconf&quot;&gt;&lt;span class=&quot;token directive-inline property&quot;&gt;Listen&lt;/span&gt; 8888&lt;br&gt;&lt;span class=&quot;token directive-inline property&quot;&gt;ServerName&lt;/span&gt; localhost:8888&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;to:&lt;/p&gt;
&lt;pre class=&quot;language-apacheconf&quot;&gt;&lt;code class=&quot;language-apacheconf&quot;&gt;&lt;span class=&quot;token directive-inline property&quot;&gt;Listen&lt;/span&gt; 80&lt;br&gt;&lt;span class=&quot;token directive-inline property&quot;&gt;ServerName&lt;/span&gt; localhost:80&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To make sure MAMP also listen to these ports, you need to change it&#39;s ports in &lt;code&gt;Preferences&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-apacheconf&quot;&gt;&lt;code class=&quot;language-apacheconf&quot;&gt;Apache Port: 80&lt;br&gt;Nginx Port: 8888&lt;br&gt;MySQL Port: 3306&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now restart MAMP and we&#39;re done. You can now use multiple projects on their own urls on your computer.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Load file contents in Eleventy</title>
    <link href="https://bnijenhuis.nl/notes/load-file-contents-in-eleventy/"/>
    <updated>2021-07-11T00:00:00Z</updated>
    <id>https://bnijenhuis.nl/notes/load-file-contents-in-eleventy/</id>
    <content type="html">&lt;p&gt;At the moment the only image on my website is my logo on the homepage. But since it&#39;s an SVG, I decided to load it directly in the HTML. I could just copy the contents of the SVG file and paste it where I wanted it. But if I decide to have the image at several different places, and I make some adjustment to it, I also would have to change it at several different places.&lt;/p&gt;
&lt;p&gt;I don&#39;t want to do that, with the risk of forgetting one. So what I want to do is to keep the SVG file, and load the contents of it dynamically. This turned out to be easier than I thought.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; fs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;fs&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addFilter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;printFileContents&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;filePath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; relativeFilePath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; filePath&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; fileContents &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readFileSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;relativeFilePath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;err&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; fileContents&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;utf8&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&#39;ve created a filter that takes the file path, finds the file, opens it and returns the contents. It requires the &lt;a href=&quot;https://nodejs.org/api/fs.html&quot;&gt;&lt;code&gt;file system&lt;/code&gt; module&lt;/a&gt;, but I already had this loaded for &lt;a href=&quot;https://bnijenhuis.nl/notes/2021-05-10-automatically-generate-open-graph-images-in-eleventy/&quot;&gt;automatically generate open graph images&lt;/a&gt; anyway.&lt;/p&gt;
&lt;p&gt;To use this filter, just add it like you would use other filters:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{{ &#39;/img/bnijenhuis.svg&#39; | printFileContents }}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result, in this case, is the full SVG code loaded in the HTML.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Implementing Service Workers with limited cache</title>
    <link href="https://bnijenhuis.nl/notes/implementing-service-workers-with-limited-cache/"/>
    <updated>2021-07-07T00:00:00Z</updated>
    <id>https://bnijenhuis.nl/notes/implementing-service-workers-with-limited-cache/</id>
    <content type="html">&lt;p&gt;When I wrote the post about &lt;a href=&quot;https://bnijenhuis.nl/notes/2021-05-03-implementing-clientside-webmentions/&quot;&gt;Implementing clientside webmentions&lt;/a&gt; a while ago it was mentioned by &lt;a href=&quot;https://twitter.com/nhoizey/status/1389630177462915081?s=20&quot;&gt;Nicolas Hoizey&lt;/a&gt; that this implementation would result in many requests to &lt;a href=&quot;https://webmention.io/&quot;&gt;webmention.io&lt;/a&gt;. And although that&#39;s the result of having a static website with dynamic webmentions, I still wanted to try and optimize this. I was already planning on implementing service workers and this was the perfect use case.&lt;/p&gt;
&lt;h2&gt;What are service workers?&lt;/h2&gt;
&lt;p&gt;I kinda knew what service workers were, but not in detail. But of course there&#39;s a lot of information available about this. I watched the &lt;a href=&quot;https://www.youtube.com/watch?v=hxiggHZOGlQ&amp;amp;list=PL4cUxeGkcC9gTxqJBcDmoi5Q2pzDusSL7&amp;amp;index=6&quot;&gt;YouTube series by The Net Ninja&lt;/a&gt;, read &lt;a href=&quot;https://web.dev/offline-cookbook/&quot;&gt;The Offline Cookbook&lt;/a&gt;, &lt;a href=&quot;https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle&quot;&gt;one&lt;/a&gt; or &lt;a href=&quot;https://developers.google.com/web/ilt/pwa/lab-caching-files-with-service-worker&quot;&gt;two&lt;/a&gt; articles on Google&#39;s developers blog and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers&quot;&gt;documentation on MDN&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;These sources will give you enough information to understand the fundamentals of service workers and what possibilities you&#39;re given as a developer.&lt;/p&gt;
&lt;h2&gt;What do I want to achieve?&lt;/h2&gt;
&lt;p&gt;As mentioned earlier the use case was to optimize the webmentions. But I had some other goals as well. The complete list was:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Serve core assets from service worker for speed purposes;&lt;/li&gt;
&lt;li&gt;Make sure the cached core assets are up to date;&lt;/li&gt;
&lt;li&gt;Cache webmention requests;&lt;/li&gt;
&lt;li&gt;Create an offline fallback when there&#39;s no connection.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Let&#39;s do it&lt;/h2&gt;
&lt;p&gt;The first step is to set up the basics. These are extensively covered in all the documentation, so I won&#39;t go too deep into it.&lt;/p&gt;
&lt;h3&gt;Setting the variables&lt;/h3&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; version &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;-v2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; coreCacheName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;core&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; version&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; apiCacheName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;api&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; version&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; coreAssets &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token string&quot;&gt;&#39;/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token string&quot;&gt;&#39;/css/style.css&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token string&quot;&gt;&#39;/fonts/Asap-Italic-VariableFont_wght.woff2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token string&quot;&gt;&#39;/fonts/Asap-VariableFont_wght.woff2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token string&quot;&gt;&#39;/img/bnijenhuis.svg&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token string&quot;&gt;&#39;/favicon.ico&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token string&quot;&gt;&#39;/offline/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token string&quot;&gt;&#39;/offline.json&#39;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; localDomains &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token string&quot;&gt;&#39;http://bnijenhuis-nl.test&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token string&quot;&gt;&#39;https://bnijenhuis.nl&#39;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First I&#39;ve defined some variables that are going to be used in the service worker. A &lt;code&gt;version&lt;/code&gt; that is used in combination with the two cache names: &lt;code&gt;coreCacheName&lt;/code&gt; (used for the core assets) and &lt;code&gt;apiCacheName&lt;/code&gt; (used for the webmention requests). By using a version in the cache names you can remove old cache when you make changes to your service workers, so you can make sure the user has the right cache.&lt;/p&gt;
&lt;p&gt;I&#39;ve also defined a &lt;code&gt;coreAssets&lt;/code&gt; array with, you guessed it, a list of all the core assets I wanted to cache. It also includes &lt;code&gt;/offline/&lt;/code&gt; and &lt;code&gt;/offline.json&lt;/code&gt; which I&#39;m going to explain later in this article.&lt;/p&gt;
&lt;p&gt;Lastly I&#39;ve defined an array of &lt;code&gt;localDomains&lt;/code&gt; which I&#39;m going to use later for checking what requests to cache.&lt;/p&gt;
&lt;h3&gt;Install the service worker&lt;/h3&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// install service worker and cache core assets&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;install&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;waitUntil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;        caches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;coreCacheName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;br&gt;            cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;coreAssets&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When installing the service worker I immediately make sure that the core assets are cached. Make sure that when using &lt;code&gt;cache.addAll()&lt;/code&gt; all the assets are available. If one is missing, the service worker will fail to install.&lt;/p&gt;
&lt;h3&gt;Activating the service worker&lt;/h3&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// make sure to remove old caches&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;activate&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;waitUntil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;        caches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Promise&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;keys&lt;br&gt;                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; key &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; coreCacheName &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; key &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; apiCacheName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; caches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;activate&lt;/code&gt; event listener will fire after the installation of the service worker. This also means it&#39;s called when the service worker is updated. And the service worker will update itself when something has been changed in it&#39;s file.&lt;/p&gt;
&lt;p&gt;I want to make sure that old caches are removed when I update the version in the service worker. So in the &lt;code&gt;activate&lt;/code&gt; event I loop through the available caches and if the cache name does not match with one of the two cache names I&#39;ve already defined, I will delete this cache.&lt;/p&gt;
&lt;h3&gt;Handling the requests&lt;/h3&gt;
&lt;p&gt;So this is where all the good stuff happens. Every request that is made on the website will go through the &lt;code&gt;fetch&lt;/code&gt; event. There are some handy tricks I&#39;m using to meet the goals I&#39;ve set earlier. Let&#39;s get into the code.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// fetch assets and serve from cache and update cache&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;fetch&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/css/style.css&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// (directly) respond with cached asset (if available)&lt;/span&gt;&lt;br&gt;        event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;respondWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;serveFromCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// update cache&lt;/span&gt;&lt;br&gt;        event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;waitUntil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;updateCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; coreCacheName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https://webmention.io/api/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// we want to limit the requests to webmention.io&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// (directly) respond with cached asset (if available)&lt;/span&gt;&lt;br&gt;        event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;respondWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;serveFromCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// (directly) respond with cached asset (if available)&lt;/span&gt;&lt;br&gt;        event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;respondWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;serveFromCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// update cache (only if in core assets)&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; requestUrl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; localDomain &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; localDomains&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            requestUrl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; requestUrl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;localDomain&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;coreAssets&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;requestUrl&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;waitUntil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;updateCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; coreCacheName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To make sure the code is still readable and to not repeat any code, I&#39;ve moved some functionality to two other functions, &lt;code&gt;serveFromCache()&lt;/code&gt; and &lt;code&gt;updateCache()&lt;/code&gt;. I&#39;ll go into this later, but let&#39;s look at what&#39;s happening in this event listener.&lt;/p&gt;
&lt;p&gt;First I&#39;m checking which requests are made, because I want to handle some of them differently. If the request is for the CSS file I&#39;ll try and serve it from the cache and after the file is served I make sure the cache is updated.&lt;/p&gt;
&lt;p&gt;If the request is for the webmentions I&#39;ll try and serve it from the cache, but I won&#39;t update the cache because I only want to update the cache once a day. More on this later on.&lt;/p&gt;
&lt;p&gt;For all the other requests I&#39;ll try and serve it from cache, and if it&#39;s a core asset I make sure the cache is updated. The request url however contains the domain, so I strip my test domain and my live domain (set in the &lt;code&gt;localDomains&lt;/code&gt; variable) from the request url to check if it&#39;s in my core asset array.&lt;/p&gt;
&lt;h3&gt;The &lt;code&gt;serveFromCache()&lt;/code&gt; function&lt;/h3&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt; * serve request from cache&lt;br&gt; * if file isn&#39;t cached longer than 24 hours, it&#39;s still valid&lt;br&gt; * @param {Event} event the request event&lt;br&gt; * @param {Boolean} ignoreSearch if true, ignore search parameters in request&lt;br&gt; * @param {Boolean} checkExpiryHeader if true, check for custom expiry header&lt;br&gt; * @return {Object} response object from cache or from fetch&lt;br&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;serveFromCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ignoreSearch&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; checkExpiryHeader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;// set the right match options&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; matchOptions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ignoreSearch&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; matchOptions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;ignoreSearch&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; caches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; matchOptions&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;cacheResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// if found return cache&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cacheResponse&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;checkExpiryHeader&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isCacheResponseStillValid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cacheResponse&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;                    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; cacheResponse&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; cacheResponse&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;        &lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// fetch it again, because cache was not found or was expired&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/css/style.css&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token function&quot;&gt;updateCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; coreCacheName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https://webmention.io/api/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token function&quot;&gt;updateCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; apiCacheName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// if offline and not found in cache, return offline data&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;endsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; caches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/offline/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https://webmention.io/api/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; caches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/offline.json&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What happens in the &lt;code&gt;serveFromCache()&lt;/code&gt; function is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Matches the request with the cache;&lt;/li&gt;
&lt;li&gt;If the &lt;code&gt;ignoreSearch&lt;/code&gt; parameter is &lt;code&gt;true&lt;/code&gt;, it will set these parameters before matching;&lt;/li&gt;
&lt;li&gt;If the &lt;code&gt;checkExpiryListener&lt;/code&gt; parameter is &lt;code&gt;true&lt;/code&gt;, and a cached file is found, the custom expiry header is checked;&lt;/li&gt;
&lt;li&gt;If no cached file is found or the cached file has expired, fetch it and save it to the right cache;&lt;/li&gt;
&lt;li&gt;If no cached file is found and the file can&#39;t be fetched, return the &lt;code&gt;offline&lt;/code&gt; page that was cache when installing the service worker. If the request was for webmention.io, return the &lt;code&gt;offline.json&lt;/code&gt; file.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;The &lt;code&gt;updateCache()&lt;/code&gt; function&lt;/h3&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt; * update cache&lt;br&gt; * @param {Object} request the event request&lt;br&gt; * @param {String} cacheName the cache to update&lt;br&gt; * @return {Object} response object&lt;br&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;updateCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cacheName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; caches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cacheName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            &lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; responseCopy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; headers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Headers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;responseCopy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;            headers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;sw-fetched-on&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; requestKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;            &lt;span class=&quot;token comment&quot;&gt;// make sure the request with query params of style.css are not saved as a different asset&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/css/style.css&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;                requestKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/css/style.css&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; responseCopy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;blob&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;requestKey&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;                    &lt;span class=&quot;token literal-property property&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; responseCopy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;status&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;                    &lt;span class=&quot;token literal-property property&quot;&gt;statusText&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; responseCopy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;statusText&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;                    &lt;span class=&quot;token literal-property property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; headers&lt;br&gt;                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What happens in the &lt;code&gt;updateCache()&lt;/code&gt; function is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Open the right cache;&lt;/li&gt;
&lt;li&gt;Fetch the file;&lt;/li&gt;
&lt;li&gt;Add the timestamp as a custom header of the file;&lt;/li&gt;
&lt;li&gt;Cache the file (make sure to cache the CSS file without the query parameters).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;The &lt;code&gt;isCacheResponseStillValid()&lt;/code&gt; function&lt;/h3&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt; * check of cacheResponse is still valid&lt;br&gt; * if file isn&#39;t cached longer than 24 hours, it&#39;s still valid&lt;br&gt; * @param {Object} cacheResponse the cacheResponse object&lt;br&gt; * @return {Boolean} if true, cacheResponse is valid&lt;br&gt; */&lt;/span&gt;&lt;br&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isCacheResponseStillValid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;cacheResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;cacheResponse&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;	&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; fetched &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; cacheResponse&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;sw-fetched-on&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;	&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;// ms * seconds * minutes * hours&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fetched &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parseFloat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fetched&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What happens in the &lt;code&gt;isCacheResponseStillValid()&lt;/code&gt; function is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Check if a cache response was found;&lt;/li&gt;
&lt;li&gt;Get the custom header with the timestamp;&lt;/li&gt;
&lt;li&gt;Check if the timestamp is older than a day. If it&#39;s not, the cache is valid. If it is, the cache should be updated.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Some handy tricks&lt;/h2&gt;
&lt;p&gt;There are some handy tricks I&#39;ve used in the service worker to reach the goals I&#39;ve set.&lt;/p&gt;
&lt;h3&gt;Handy trick #1: Ignore query parameters in request&lt;/h3&gt;
&lt;p&gt;If you&#39;ve read my note &lt;a href=&quot;https://bnijenhuis.nl/notes/2021-04-23-cache-busting-in-eleventy/&quot;&gt;Cache busting in Eleventy&lt;/a&gt; you might remember that I&#39;m using a query parameter added to my CSS file, like &lt;code&gt;/css/style.css?v=1623355015&lt;/code&gt;, to make sure the browser cache won&#39;t serve an older version of the stylesheet. This however was causing a problem in combination with the service worker. If you look at the list of core assets I defined earlier, you can see that it contains &lt;code&gt;/css/style.css&lt;/code&gt;. By default this means the service worker won&#39;t recognize the &lt;code&gt;/css/style.css?v=1623355015&lt;/code&gt; request as cached, because strictly speaking it&#39;s another request.&lt;/p&gt;
&lt;p&gt;While matching the request to the cached assets it&#39;s possible to add extra parameters to ignore these query parameters. So to make sure that the request for &lt;code&gt;/css/style.css?v=1623355015&lt;/code&gt; will return the cached asset &lt;code&gt;/css/style.css&lt;/code&gt;, I&#39;m using these parameters as such: &lt;code&gt;caches.match(event.request, {ignoreSearch:true}})&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Handy trick #2: Serve first, update second&lt;/h3&gt;
&lt;p&gt;To speed up the website I wanted to serve assets from the cache (if available). But I also wanted to make sure the cached assets are up to date. This was relatively easy to implement by using the code on &lt;a href=&quot;https://serviceworke.rs/strategy-cache-and-update_service-worker_doc.html&quot;&gt;serviceworke.rs&lt;/a&gt;. It uses &lt;code&gt;respondWith()&lt;/code&gt; to immediately serve the request from cache, followed by &lt;code&gt;waitUntil()&lt;/code&gt; to update the cache in the background. I only want to update the core assets though, so I&#39;ve added a check for this.&lt;/p&gt;
&lt;h3&gt;Handy trick #3: Control the cache expiry&lt;/h3&gt;
&lt;p&gt;If a service worker caches a file, it&#39;s cached. There&#39;s no expiry header for these files. But in order to reach my goal of optimizing the requests to webmention.io I wanted to cache these requests for a limited period of time. My plan was to cache the webmention.io request for a day, so if a single user visits an article it will only make this request once a day.&lt;/p&gt;
&lt;p&gt;I found &lt;a href=&quot;https://gomakethings.com/how-to-set-an-expiration-date-for-items-in-a-service-worker-cache/&quot;&gt;this Go Make Things article&lt;/a&gt; which does exactly that. In short it intercepts the request and adds a header with a timestamp to it before caching it. When fetching a request from the cache it checks this header to see if it&#39;s still valid or not.&lt;/p&gt;
&lt;h3&gt;Bonus trick: Using service workers on your development environment&lt;/h3&gt;
&lt;p&gt;For service workers to work an HTTPS connection is required. There is an exception for &lt;code&gt;http://localhost[:port]&lt;/code&gt; and &lt;code&gt;http://127.x.y.z[:port]&lt;/code&gt; however, but if you&#39;re using custom domains for your development environments (like I am) you can&#39;t use service workers.&lt;/p&gt;
&lt;p&gt;Luckily you can change some settings in Chrome and Firefox to enable service workers on HTTP connections. &lt;a href=&quot;https://stackoverflow.com/questions/34160509/options-for-testing-service-workers-via-http&quot;&gt;Stack Overflow&lt;/a&gt; to the rescue!&lt;/p&gt;
&lt;h2&gt;Loading the service worker&lt;/h2&gt;
&lt;p&gt;It took a bit of diving into the documentation, searching for references and (of course) some trial and error, but eventually I reached all of my goals. Only thing left is to make sure the website loads the service worker. This is pretty straight forward:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;serviceWorker&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;      navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;serviceWorker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/sw.js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;if&lt;/code&gt; statement is to check if the browser is supporting service workers.&lt;/p&gt;
&lt;p&gt;And that&#39;s it. The service worker is finished. The website will cache all the core assets when installing the service worker, it will serve from cache if possible, it will update the cache in background and it has a limited cache for certain requests. You can find the complete code on &lt;a href=&quot;https://github.com/bnijenhuis/bnijenhuis-nl/blob/212750238b0f6dd10e9e61296c00f58c9593046e/sw.js&quot;&gt;GitHub&lt;/a&gt;. If you have any questions or remarks, please hit me up on &lt;a href=&quot;https://twitter.com/bnijenhuis&quot;&gt;Twitter&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Automatically generate open graph images in Eleventy</title>
    <link href="https://bnijenhuis.nl/notes/automatically-generate-open-graph-images-in-eleventy/"/>
    <updated>2021-05-10T00:00:00Z</updated>
    <id>https://bnijenhuis.nl/notes/automatically-generate-open-graph-images-in-eleventy/</id>
    <content type="html">&lt;p&gt;When sharing links on social media, it&#39;s nice to have an image instead of just the link. To achieve this you can implement the &lt;code&gt;og:&lt;/code&gt; meta tags. You can check out the &lt;a href=&quot;https://ogp.me/&quot;&gt;Open Graph protocol&lt;/a&gt; for more information about all the available tags. To define this image you can use  the &lt;code&gt;og:image&lt;/code&gt; meta tag. This can be a static image of the logo of your website for example, but for articles it&#39;s nicer to have the image contain the title and the date of the article.&lt;/p&gt;
&lt;p&gt;Ofcourse you can create these manually, but that&#39;s a lot of work. It would be way easier to have them generated automatically whenever you add an article. So let&#39;s do this.&lt;/p&gt;
&lt;h2&gt;Getting started&lt;/h2&gt;
&lt;p&gt;While researching implementations of other people I came across the article &lt;a href=&quot;https://fettblog.eu/11ty-automatic-twitter-cards/&quot;&gt;11ty: Generate Twitter cards automatically&lt;/a&gt; by Stefan Baumgartner which comes awfully close to what I&#39;m trying to achieve. The only thing I want to do differently is that I want to solely rely on Eleventy for this, no other tools. So where he uses Gulp to ultimately generate the &lt;code&gt;.jpeg&lt;/code&gt; files, I want to use Eleventy for this as well.&lt;/p&gt;
&lt;p&gt;What I want to achieve is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a SVG for every post;&lt;/li&gt;
&lt;li&gt;Convert the SVG to a JPEG;&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;meta&lt;/code&gt; tags to website.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Create a SVG for every post&lt;/h2&gt;
&lt;p&gt;The advantage of SVG is that you can create an image by code. This makes it perfect to automatically create a base file for the Open Graph image. I have to create this for every single post. To do this we can use the &lt;code&gt;collections&lt;/code&gt; functionality in Eleventy combined with the &lt;code&gt;pagination&lt;/code&gt; parameter. Every post is stored in a &lt;code&gt;collection&lt;/code&gt; in Eleventy and by setting the &lt;code&gt;pagination&lt;/code&gt; parameter to &lt;code&gt;1&lt;/code&gt; it will generate a new page for every post.&lt;/p&gt;
&lt;p&gt;I created a new file to create the SVG&#39;s with the following front matter:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;---&lt;br&gt;pagination:&lt;br&gt;  data: collections.notes&lt;br&gt;  size: 1&lt;br&gt;  alias: preview&lt;br&gt;permalink: &quot;/img/social-preview-images/{{ preview.data.date | postDate }}-{{ preview.data.pageTitle | slug }}-preview.svg&quot;&lt;br&gt;eleventyExcludeFromCollections: true&lt;br&gt;---&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;alias&lt;/code&gt; parameter sets the variable name which contains all the information of the post. The &lt;code&gt;permalink&lt;/code&gt; parameter sets the location to which the posts are saved (the &lt;code&gt;postDate&lt;/code&gt; filter is a filter that formats the date in a &lt;code&gt;yyyy-mm-dd&lt;/code&gt; format). And lastly the &lt;code&gt;eleventyExcludeFromCollections&lt;/code&gt; set to &lt;code&gt;true&lt;/code&gt; makes sure to not include these files in other collections.&lt;/p&gt;
&lt;p&gt;Here&#39;s the full file I use to create the SVG&#39;s. I&#39;ll highlight some items after the code.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;---&lt;br&gt;pagination:&lt;br&gt;  data: collections.notes&lt;br&gt;  size: 1&lt;br&gt;  alias: preview&lt;br&gt;permalink: &quot;/img/social-preview-images/{{ preview.data.date | postDate }}-{{ preview.data.pageTitle | slug }}-preview.svg&quot;&lt;br&gt;eleventyExcludeFromCollections: true&lt;br&gt;---&lt;br&gt;&lt;span class=&quot;token prolog&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;svg&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;1200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;628&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;viewBox&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0 0 1200 628&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;1.1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;xmlns&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;http://www.w3.org/2000/svg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;xmlns:&lt;/span&gt;xlink&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;http://www.w3.org/1999/xlink&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    {% set titleInLines = preview.data.pageTitle | splitlines %}&lt;br&gt;    {% set numberOfLines = titleInLines.length %}&lt;br&gt;    {% if numberOfLines == 1 %}&lt;br&gt;        {% set verticalStartingPoint = 340 %}&lt;br&gt;    {% elseif numberOfLines == 2 %}&lt;br&gt;        {% set verticalStartingPoint = 290 %}&lt;br&gt;    {% elseif numberOfLines == 3 %}&lt;br&gt;        {% set verticalStartingPoint = 250 %}&lt;br&gt;    {% elseif numberOfLines == 4 %}&lt;br&gt;        {% set verticalStartingPoint = 210 %}&lt;br&gt;    {% endif %}&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;rect&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;100%&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;100%&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;white&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;text&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;text-anchor&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;start&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#39;&lt;/span&gt;Giant Head OT&lt;span class=&quot;token punctuation&quot;&gt;&#39;&lt;/span&gt;, Helvetica, sans-serif&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;tspan&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;80&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;350&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;bn&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;tspan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;line&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;x1&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;300&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;y1&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;80&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;x2&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;300&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;y2&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;548&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;stroke&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;black&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;text&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Asap, Helvetica, sans-serif&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;30&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;tspan&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;350&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;{{verticalStartingPoint - 90}}&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ preview.date | readablePostDate }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;tspan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;text&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Asap, Helvetica, sans-serif&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;80&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;bold&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    {% for line in titleInLines %}&lt;br&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;tspan&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;350&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;{{verticalStartingPoint + loop.index0 * 80}}&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{line}}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;tspan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    {% endfor %}&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;svg&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;The splitlines filter&lt;/h3&gt;
&lt;p&gt;Because SVG doesn&#39;t support multiline texts we need to do this ourselves. This is a filter I copied from the article I mentioned before. It splits up the given text by words (the page title in this case) and creates an array of lines depending of the maximum size of characters per line (19 in my case).&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;eleventyConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;br&gt;    eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addFilter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;splitlines&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; parts &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; input&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39; &#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; lines &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; parts&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;prev&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; current&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;prev&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;current&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;        &lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; lastOne &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; prev&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;prev&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lastOne&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; current&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;19&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;prev&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; current&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;        prev&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;prev&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; lastOne &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39; &#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; current&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; prev&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; lines&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Setting the right vertical position per line&lt;/h3&gt;
&lt;p&gt;I want the text to be centered vertically so I needed to calculate the vertical starting point. Because a title that uses 4 lines has a different starting point than a title that uses just 1 line. This comes down to a bit of trial and error. Just change it and see if it&#39;s what you want.&lt;/p&gt;
&lt;p&gt;While looping through the lines I add to the defined starting point to make sure the following line isn&#39;t printed directly over the previous line, but it renders below the previous line.&lt;/p&gt;
&lt;h2&gt;Convert the SVG to a JPEG&lt;/h2&gt;
&lt;p&gt;So now that I have the SVG&#39;s generated for each post, I need to convert these to JPEG&#39;s (because Open Graph doesn&#39;t support SVG&#39;s in their &lt;code&gt;image&lt;/code&gt; tag). This is where the Eleventy &lt;a href=&quot;https://www.11ty.dev/docs/plugins/image/&quot;&gt;Image plugin&lt;/a&gt; comes in to play. This plugin can - among other things - convert images, for instance SVG images to JPEG images.&lt;/p&gt;
&lt;p&gt;To convert the SVG&#39;s to JPEG&#39;s I&#39;ve added the following code to my &lt;code&gt;.eleventy.js&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; fs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;fs&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; Image &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;@11ty/eleventy-img&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;eleventyConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;br&gt;    eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;afterBuild&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; socialPreviewImagesDir &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;_site/img/social-preview-images/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readdir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;socialPreviewImagesDir&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;err&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; files&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;files&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;                files&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;                    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;filename&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;endsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;.svg&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;                        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; imageUrl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; socialPreviewImagesDir &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; filename&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;                        &lt;span class=&quot;token function&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;imageUrl&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;                            &lt;span class=&quot;token literal-property property&quot;&gt;formats&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;jpeg&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;                            &lt;span class=&quot;token literal-property property&quot;&gt;outputDir&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; socialPreviewImagesDir&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;                            &lt;span class=&quot;token function-variable function&quot;&gt;filenameFormat&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; src&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; width&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; format&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;                                &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; outputFilename &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; filename&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;substring&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;filename&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;                            &lt;br&gt;                                &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;outputFilename&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;format&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;                            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;                        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&#39;m making use of the &lt;a href=&quot;https://www.11ty.dev/docs/events/#afterbuild&quot;&gt;&lt;code&gt;afterBuild&lt;/code&gt; event&lt;/a&gt; of Eleventy. This allows me to parse the generated SVG&#39;s and convert them to JPEG&#39;s. What happens in the code above is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It defines the directory where the SVG images are stored;&lt;/li&gt;
&lt;li&gt;It reads this directory and loop through the files in it using the &lt;a href=&quot;https://nodejs.org/api/fs.html&quot;&gt;&lt;code&gt;file system&lt;/code&gt; module&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;It only parses files with an &lt;code&gt;.svg&lt;/code&gt; extension, because I&#39;m saving the JPEG files in the same folder;&lt;/li&gt;
&lt;li&gt;It uses the Eleventy &lt;a href=&quot;https://www.11ty.dev/docs/plugins/image/&quot;&gt;Image plugin&lt;/a&gt; to convert the .svg to a .jpg file.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Using webfonts&lt;/h3&gt;
&lt;p&gt;You can use webfonts in the SVG&#39;s. At first I defined the webfonts in the &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tag in the SVG. This works fine in the SVG, but when I converted it to a JPEG file, it didn&#39;t use the defined font. The Image plugin doesn&#39;t parse the fonts in the SVG and therefor uses a system font instead. The easiest solution to this is to install the fonts on your system. Now the fonts are parsed correctly when generating the JPEG file.&lt;/p&gt;
&lt;h2&gt;Add generated image to &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt; tags&lt;/h2&gt;
&lt;p&gt;Now that I have the generated image, I need to add this to the &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt; tags of the page. For now I&#39;ve only generated these images for my notes, which are the only pages with tags. I&#39;ve created a default fallback for other pages. This results in the following code:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;meta property=&quot;og:title&quot; content=&quot;{% if page.url == &quot;/&quot; %}Bernard Nijenhuis • Front-end Developer{% else %}{{ pageTitle }}{% endif %}&quot; /&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;og:url&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;{{ page.url }}&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;{% if tags %}&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;og:image&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://bnijenhuis.nl/img/social-preview-images/{{ page.date | postDate }}-{{ pageTitle | slug }}-preview.jpeg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;og:image:secure_url&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://bnijenhuis.nl/img/social-preview-images/{{ page.date | postDate }}-{{ pageTitle | slug }}-preview.jpeg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;{% else %}&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;og:image&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://bnijenhuis.nl/img/default-preview.jpeg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;og:image:secure_url&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://bnijenhuis.nl/img/default-preview.jpeg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;{% endif %}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&#39;m setting the &lt;code&gt;og:title&lt;/code&gt; to the page title, except for the homepage, because I want that to be different. The &lt;code&gt;og:url&lt;/code&gt; is set to the current url. The &lt;code&gt;og:image&lt;/code&gt; is set to the generated JPEG file if the page has tags, otherwise it will be the default image I created. Make sure to make this url absolute, or else it won&#39;t be parsed correctly.&lt;/p&gt;
&lt;h3&gt;Twitter specific &lt;code&gt;meta&lt;/code&gt; tags&lt;/h3&gt;
&lt;p&gt;To optimize this for Twitter there are a couple of exta &lt;code&gt;meta&lt;/code&gt; tags needed. The images that are generated are 1200 pixels wide and 628 pixel high (this is a 16:9 ratio). This is the recommended size for an &amp;quot;Image from a Tweet with shared link&amp;quot; according to &lt;a href=&quot;https://sproutsocial.com/insights/social-media-image-sizes-guide/&quot;&gt;Sprout Social&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The specific &lt;code&gt;meta&lt;/code&gt; tags for Twitter are:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;twitter:card&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;summary_large_image&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;twitter:site&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;@bnijenhuis&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;twitter:creator&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;@bnijenhuis&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The value &#39;summary_large_image&#39; makes sure the image is displayed above the Twitter card, instead of as a small thumbnail on the left of the card.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I&#39;ve now taken all the steps to automatically generate the Open Graph images in Eleventy, without using any external tools. You can find my specific implementation on &lt;a href=&quot;https://github.com/bnijenhuis/bnijenhuis-nl&quot;&gt;GitHub&lt;/a&gt;. If you have any questions or remarks, please hit me up on &lt;a href=&quot;https://twitter.com/bnijenhuis&quot;&gt;Twitter&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Implementing clientside webmentions</title>
    <link href="https://bnijenhuis.nl/notes/implementing-clientside-webmentions/"/>
    <updated>2021-05-03T00:00:00Z</updated>
    <id>https://bnijenhuis.nl/notes/implementing-clientside-webmentions/</id>
    <content type="html">&lt;p&gt;I came across webmentions a while ago and I wanted to try it out. If you don&#39;t know what a webmention is, &lt;a href=&quot;https://en.wikipedia.org/wiki/Webmention&quot;&gt;Wikipedia&lt;/a&gt; describes it as follows:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Webmention enables authors to keep track of who is linking to, referring to, or commenting on their articles. By incorporating such comments from other sites, sites themselves provide federated commenting functionality.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;For a static website as the one you&#39;re on right now, webmentions enable me to load likes, reposts and replies I get on for example Twitter in the page itself. Now there are several articles about implementing webmentions, but I hit a few bumps in the road while implementing it. I&#39;ll try to explain everything step by step in this note so you can avoid those bumps. I used the articles &lt;a href=&quot;https://mxb.dev/blog/using-webmentions-on-static-sites/&quot;&gt;Using Webmentions in Eleventy&lt;/a&gt; by Max Böck and &lt;a href=&quot;https://www.swyx.io/clientside-webmentions/&quot;&gt;Clientside Webmentions&lt;/a&gt; by Shawn &amp;quot;swyx&amp;quot; Wang as references.&lt;/p&gt;
&lt;h2&gt;Enable webmentions for your website&lt;/h2&gt;
&lt;p&gt;If you want to include webmentions on your website you&#39;ll need a service that collects all the webmentions for you. &lt;a href=&quot;https://webmention.io/&quot;&gt;Webmention.io&lt;/a&gt; is a service that does that for you for free. To register your website with Webmention.io, your homepage and social media profiles need to link to each other for verification. To do this you need to follow these steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add &lt;a href=&quot;https://indieweb.org/rel-me&quot;&gt;rel-me&lt;/a&gt; links to your homepage for the various ways to reach you. You used to be able to verify via Twitter and Instagram, but for now only GitHub seems to be supported. Make sure the link to your GitHub profile has the &lt;code&gt;rel&lt;/code&gt;-attribute set to the value &lt;code&gt;me&lt;/code&gt;, e.g. &lt;code&gt;&amp;lt;a href=&amp;quot;https://github.com/bnijenhuis&amp;quot; rel=&amp;quot;me&amp;quot;&amp;gt;GitHub&amp;lt;/a&amp;gt;&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;Every social media profile you linked to must have a link back to your website in your profile. This is used for the verification process;&lt;/li&gt;
&lt;li&gt;Finally, include &lt;code&gt;&amp;lt;link rel=&amp;quot;authorization_endpoint&amp;quot; href=&amp;quot;https://indieauth.com/auth&amp;quot;&amp;gt;&lt;/code&gt; on your homepage.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you have followed these steps you can sign in your website at &lt;a href=&quot;https://webmention.io/&quot;&gt;Webmention.io&lt;/a&gt;. After signing in you will see the supported providers (the social media links you provided with the rel-me attribute) with buttons to verify them. Click on the button of the provider to finalize the verification process. The last thing you have to do to make sure your website can accept webmentions is to add the webmention and pingback metatags to your website. You can find them on the &lt;a href=&quot;https://webmention.io/settings&quot;&gt;settings page&lt;/a&gt; of Webmention.io after you signed in. These are the tags I use on my website, yours will be similar:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;webmention&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://webmention.io/bnijenhuis.nl/webmention&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;pingback&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://webmention.io/bnijenhuis.nl/xmlrpc&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Link your social media&lt;/h2&gt;
&lt;p&gt;Now your website has been registered to collect webmentions, but what you really want is to have them automatically collected from your social media. &lt;a href=&quot;https://brid.gy/&quot;&gt;Brid.gy&lt;/a&gt; is a service that does that for you, also for free. If you want Bridgy to check your Twitter account for webmentions, you just need to click the Twitter button and authorize Bridgy. It will directly start to analyze your tweets, but you can submit specific tweets as well.&lt;/p&gt;
&lt;h2&gt;Load the webmentions on your website&lt;/h2&gt;
&lt;p&gt;You&#39;ve now enabled your website for webmentions and linked your social media so the webmentions are automatically collected. Now all you need to do is show them on your website. Webmention.io has an API you can use for this. For my website I&#39;m using two API endpoints, the &lt;code&gt;count&lt;/code&gt; endpoint and the &lt;code&gt;mentions&lt;/code&gt; endpoint.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;count&lt;/code&gt; endpoint gives a summary of the number of all the webmentions as a total and split up by type of webmentions. This endpoint requires a &lt;code&gt;target&lt;/code&gt; parameter, which is the full url of the page you want the webmentions of. For instance:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; postUrl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://bnijenhuis.nl/notes/2021-04-30-implementing-clientside-webmentions/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://webmention.io/api/count?target=&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; postUrl&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;responseJson&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;br&gt;        console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;responseJson&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// do whatever you like with it&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will return something like this:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token property&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;17&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;like&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;mention&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;reply&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;repost&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;mentions&lt;/code&gt; endpoint gives you all the details of the webmentions. This endpoint requires the same &lt;code&gt;target&lt;/code&gt; parameter as the previous endpoint and can be extended with some parameters for sorting the webmentions. For instance:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; postUrl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://bnijenhuis.nl/notes/2021-04-30-implementing-clientside-webmentions/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://webmention.io/api/mentions.jf2?target=&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; postUrl &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;amp;sort-by=published&amp;amp;sort-dir=up&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;responseJson&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;br&gt;        console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;responseJson&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// do whatever you like with it&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will return something like this (this is an excerpt from some webmentions on my previous post):&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;feed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Webmentions&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token property&quot;&gt;&quot;children&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;entry&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;author&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;card&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Behdad Esfahbod&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token property&quot;&gt;&quot;photo&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://webmention.io/avatar/pbs.twimg.com/65203a0b3d791846c880319f60dcdb423fc4246ea31f51b82c4ddc6c7819329f.jpg&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token property&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://twitter.com/behdadesfahbod&quot;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://twitter.com/bnijenhuis/status/1382593304701833216#favorited-by-15402347&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;published&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token null keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;wm-received&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2021-04-26T21:06:28Z&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;wm-id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1133909&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;wm-source&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://brid.gy/like/twitter/bnijenhuis/1382593304701833216/15402347&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;wm-target&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://bnijenhuis.nl/notes/2021-04-13-how-to-add-self-hosted-variable-fonts-to-your-website/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;like-of&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://bnijenhuis.nl/notes/2021-04-13-how-to-add-self-hosted-variable-fonts-to-your-website/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;wm-property&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;like-of&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;wm-private&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;entry&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;author&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;card&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Bram.us&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token property&quot;&gt;&quot;photo&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://webmention.io/avatar/pbs.twimg.com/2d58128eca8ca19f950d63446a08ff738b7629792495ae912ca559c3b5aa9503.jpg&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token property&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://twitter.com/bramusblog&quot;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://twitter.com/bramusblog/status/1386782975006740480&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;published&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2021-04-26T20:43:21+00:00&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;wm-received&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2021-04-26T21:08:16Z&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;wm-id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1133911&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;wm-source&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://brid.gy/comment/twitter/bnijenhuis/1386782265552801801/1386782975006740480&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;wm-target&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://bnijenhuis.nl/notes/2021-04-13-how-to-add-self-hosted-variable-fonts-to-your-website/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token property&quot;&gt;&quot;html&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Cool! 👌\n&amp;lt;a class=\&quot;u-mention\&quot; href=\&quot;https://bnijenhuis.nl/\&quot;&gt;&amp;lt;/a&gt;\n&amp;lt;a class=\&quot;u-mention\&quot; href=\&quot;https://twitter.com/bnijenhuis\&quot;&gt;&amp;lt;/a&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token property&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Cool! 👌&quot;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;in-reply-to&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://bnijenhuis.nl/notes/2021-04-13-how-to-add-self-hosted-variable-fonts-to-your-website/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;wm-property&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;in-reply-to&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;wm-private&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that&#39;s it. For a specific (Eleventy) implementation you can find my implementation on &lt;a href=&quot;https://github.com/bnijenhuis/bnijenhuis-nl/blob/main/_includes/webmentions.liquid&quot;&gt;GitHub&lt;/a&gt;. If you have any questions or remarks, please hit me up on &lt;a href=&quot;https://twitter.com/bnijenhuis/&quot;&gt;Twitter&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Paging&lt;/h2&gt;
&lt;p&gt;By default the &lt;code&gt;mentions&lt;/code&gt; endpoint returns a maximum of 20 entries. This means that of you have more than 20 webmentions, not all of them will show. There&#39;s a parameter to override the default though. It&#39;s the &lt;code&gt;per-page&lt;/code&gt; parameter. You can set this to a value that you think will always include all webmentions, but since I already made the call to get the total count of webmentions I can get that specific count of webmentions. With some minor tweaks to the code I&#39;ve ended up with this:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; postUrl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://bnijenhuis.nl/notes/2021-04-30-implementing-clientside-webmentions/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://webmention.io/api/count?target=&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; postUrl&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;responseJson&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;br&gt;        &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://webmention.io/api/mentions.jf2?target=&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; postUrl &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;amp;sort-by=published&amp;amp;sort-dir=up&amp;amp;per-page=&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; responseJson&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;responseJson&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;br&gt;            console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;responseJson&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// do whatever you like with it&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
</content>
  </entry>
  
  <entry>
    <title>Cache busting in Eleventy</title>
    <link href="https://bnijenhuis.nl/notes/cache-busting-in-eleventy/"/>
    <updated>2021-04-23T00:00:00Z</updated>
    <id>https://bnijenhuis.nl/notes/cache-busting-in-eleventy/</id>
    <content type="html">&lt;p&gt;Because I&#39;m still following my &lt;a href=&quot;https://bnijenhuis.nl/notes/2021-03-01-unhurried-development/&quot;&gt;Unhurried Development&lt;/a&gt; principle I&#39;m taking small steps and making small changes. That&#39;s how I came across an issue with cached assets&lt;small&gt;&lt;sup&gt;[1]&lt;/sup&gt;&lt;/small&gt;. A cached CSS file in my case, to be specific. And although it&#39;s easy to force reload on desktop browsers, this is a lot harder on mobile devices. Besides, visitors don&#39;t know they are using a CSS file that is served from cache, so they only see the changes when the cache expires.&lt;/p&gt;
&lt;p&gt;To make sure the file isn&#39;t served from the cache, the url of the file needs to change. But I don&#39;t want to change the file url manually every time I&#39;ve changed the file. But you know what automatically changes when changing a file? That&#39;s right! The datetime modified of the file itself!&lt;/p&gt;
&lt;p&gt;This probably has been done before in Eleventy, so while searching for a solution I came across &lt;a href=&quot;https://rob.cogit8.org/posts/2020-10-28-simple-11ty-cache-busting/&quot;&gt;a cache busting article for Eleventy&lt;/a&gt; by Rob Hudson. He basically had the same idea as I had and created a filter for Eleventy. But instead of getting the datetime modified of the file, he used the current datetime. This was a good starting point for me, as I&#39;m kinda new to creating filters in Eleventy. So now I had the following filter:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addFilter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;bust&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;urlPart&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; paramPart&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;?&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; params &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URLSearchParams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;paramPart &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    params&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;v&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; DateTime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;X&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;urlPart&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;params&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I could have a unique url for my CSS file by using this filter like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{{ &#39;/css/style.css&#39; | url | bust }}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;which would render into something like this:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/css/style.css?v=1604094309&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next step was to find a way to get the datetime modified of the file. That&#39;s where the &lt;a href=&quot;https://nodejs.org/api/fs.html&quot;&gt;file system module&lt;/a&gt; comes into play. By using this module I can interact with the file system. To get the datetime modified of a file I needed the &lt;code&gt;fs.statSync&lt;/code&gt; function which accepts a &lt;code&gt;path&lt;/code&gt; parameter of the file that you want the statistics of. The &lt;code&gt;path&lt;/code&gt; to use should be relative though and the &lt;code&gt;path&lt;/code&gt; given is absolute, so I needed to fix this as well. Furthermore I placed it in a &lt;code&gt;try catch&lt;/code&gt; block, just in case something goes wrong and it ruins the whole build.&lt;/p&gt;
&lt;p&gt;With all these modifications I ended up with the following filter:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; fs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;fs&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;eleventyConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addFilter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;bust&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;urlPart&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; paramPart&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;?&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; params &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URLSearchParams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;paramPart &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; relativeUrl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;urlPart&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;charAt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; urlPart&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;substring&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; urlPart&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; fileStats &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;statSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;relativeUrl&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dateTimeModified &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fileStats&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mtime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;X&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;            params&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;v&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dateTimeModified&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;            &lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;urlPart&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;params&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When using this filter it will:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Split up the url on the &lt;code&gt;?&lt;/code&gt; character, just in case some url parameters were already given;&lt;/li&gt;
&lt;li&gt;Create a variable with the given url parameters;&lt;/li&gt;
&lt;li&gt;Make sure the url is relative;&lt;/li&gt;
&lt;li&gt;Find the file and get the timestamp of the datetime modified;&lt;/li&gt;
&lt;li&gt;Added the timestamp to the url parameters;&lt;/li&gt;
&lt;li&gt;Add the new url parameters to the file url.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now my CSS file url will only update if the CSS file itself has been changed.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;small&gt;&lt;sup&gt;[1]&lt;/sup&gt;&lt;/small&gt; I should point out that if you&#39;re on the JAMstack, for instance Netlify, this probably won&#39;t be a problem, because they often handle it for you.&lt;/em&gt;&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>How to add self-hosted variable fonts to your website</title>
    <link href="https://bnijenhuis.nl/notes/how-to-add-self-hosted-variable-fonts-to-your-website/"/>
    <updated>2021-04-13T00:00:00Z</updated>
    <id>https://bnijenhuis.nl/notes/how-to-add-self-hosted-variable-fonts-to-your-website/</id>
    <content type="html">&lt;p&gt;To give my website a bit more personality I wanted to use a webfont instead of a system font. And because variable fonts are gaining traction I thought I would give it a shot.&lt;/p&gt;
&lt;p&gt;If you&#39;re not familiar with variable fonts, I would suggest you read the &lt;a href=&quot;https://web.dev/variable-fonts/&quot;&gt;Introduction to variable fonts on the web&lt;/a&gt;. In short, variable fonts can be described by the following quote from the mentioned article:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Many font families offer a much wider range of styles, from Thin to Black weights, narrow and wide widths, a variety of stylistic details, and even size-specific designs (optimized for large or small text sizes.) Since you&#39;d have to load a new font file for every style (or combinations of styles), many web developers choose not to use these capabilities, reducing the reading experience of their users.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Variable fonts address these challenges, by packing styles into a single file.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There are quite a lot of variable fonts available on &lt;a href=&quot;https://fonts.google.com/?vfonly=true&quot;&gt;Google Fonts&lt;/a&gt;, so there&#39;s a lot to choose from these days. When you&#39;ve made your choice you can download the font family from Google Fonts. By doing this you get the &lt;abbr title=&quot;True Type Font&quot;&gt;TTF&lt;/abbr&gt; file(s) of the variable font. Normally I would run them through &lt;a href=&quot;https://www.fontsquirrel.com/&quot;&gt;Font Squirrel&lt;/a&gt;, &lt;a href=&quot;https://transfonter.org/&quot;&gt;Transfonter&lt;/a&gt; or some other online tool to create a WOFF2 file that I can use as a webfont. But these tools aren&#39;t capable of handling variable fonts.&lt;/p&gt;
&lt;h2&gt;So what now?&lt;/h2&gt;
&lt;p&gt;I decided to drop a message to &lt;a href=&quot;https://twitter.com/PixelAmbacht&quot;&gt;Roel Nieskens&lt;/a&gt;. He&#39;s the creator of &lt;a href=&quot;https://wakamaifondue.com/&quot;&gt;Wakamai Fondue&lt;/a&gt;, is a co-author of the aforementioned article, does a lot of stuff that is font related, and he&#39;s Dutch...so yeah. He suggested I use a &lt;a href=&quot;https://github.com/google/woff2&quot;&gt;woff2 tool by Google&lt;/a&gt; to convert the TTF file to WOFF2. I also found &lt;a href=&quot;https://henry.codes/writing/how-to-convert-variable-ttf-font-files-to-woff2/&quot;&gt;an article by Henry Desroches&lt;/a&gt; describing step by step how to use that same tool. This worked just as described so I really recommend this tool (and article for that matter).&lt;/p&gt;
&lt;h2&gt;Filesize&lt;/h2&gt;
&lt;p&gt;You&#39;d think the filesize of a variable font would be considerably larger because it contains all the styles in one file. And if I compare the size of the variable font file to the size of one of the static font files, there certainly is a difference. For instance, I&#39;m using the &lt;a href=&quot;https://fonts.google.com/specimen/Asap&quot;&gt;Asap&lt;/a&gt; font family and the filesize of the variable font is 243kb while the filesize of the regular static font file is 148kb. But so is the medium file, the semi bold file, the bold file, etcetera. So all in all the variable font is already relatively small.&lt;/p&gt;
&lt;p&gt;When I converted the variable font to a WOFF2 file, the filesize went down to only 56kb. That&#39;s pretty small considering it contains all the weights of the font.&lt;/p&gt;
&lt;h2&gt;How to implement it&lt;/h2&gt;
&lt;p&gt;Now that we have the converted variable font, we only need to add it to the stylesheet. It&#39;s mostly the same as you would implement a static font. Here is the font-face declaration I use:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@font-face&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Asap&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&#39;/fonts/Asap-VariableFont_wght.woff2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;woff2 supports variations&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;         &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&#39;/fonts/Asap-VariableFont_wght.woff2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;woff2-variations&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 400 700&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token property&quot;&gt;font-display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; swap&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token property&quot;&gt;font-style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; normal&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you look at the &lt;code&gt;src&lt;/code&gt; property you can see I use two types of &lt;code&gt;format&lt;/code&gt; declarations. The first one is the format that will be the default in the future for all browsers and the second one is the soon to be deprecated format.&lt;/p&gt;
&lt;p&gt;You might notice the &lt;code&gt;font-weight&lt;/code&gt; property is defined by two values. This means that this particular font supports weights between 400 and 700. Some variable fonts also have a &lt;code&gt;font-stretch&lt;/code&gt; property and you can add this as well. To find out what values you should use with your variable fonts you can upload the font to &lt;a href=&quot;https://wakamaifondue.com/&quot;&gt;Wakamai Fondue&lt;/a&gt; to get all the specifications of your font.&lt;/p&gt;
&lt;h2&gt;Fallbacks&lt;/h2&gt;
&lt;p&gt;Variable fonts are &lt;a href=&quot;https://caniuse.com/variable-fonts&quot;&gt;pretty well supported&lt;/a&gt;. The nice thing about variable fonts is that it will always provide one style as a built in fallback so it works on non variable fonts supporting systems as a static font. Mostly it will be the regular style, but it can differ per font.&lt;/p&gt;
&lt;p&gt;If the regular style is the built in fallback and the browser needs to render a bold text, it might create a faux bold (this depends on the browser). It&#39;s not always what you want, but for a fallback it might be enough. If you don&#39;t want the browser to create the faux bold you will need to include the static bold font file. Also, when the built in fallback isn&#39;t the style you want (because for some obscure reason the fallback style is italic or something), you have to load the static font separately as well.&lt;/p&gt;
&lt;p&gt;For the &lt;code&gt;Asap&lt;/code&gt; font the built in fallback style is the regular style. So for non supporting browsers declaring only the variable font will result in text rendered in the regular style. If you want the other styles rendered as they&#39;re supposed to you&#39;ll need the define all the styles first. Then follow up with a &lt;code&gt;@supports&lt;/code&gt; declaration for the variable font, like below, to load the variable font for supporting browsers:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@font-face&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Asap&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&#39;/fonts/Asap-Regular.woff2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;woff2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; normal&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token property&quot;&gt;font-display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; swap&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token property&quot;&gt;font-style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; normal&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@font-face&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Asap&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&#39;/fonts/Asap-Bold.woff2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;woff2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bold&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token property&quot;&gt;font-display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; swap&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token property&quot;&gt;font-style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; normal&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@supports&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;font-variation-settings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; normal&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@font-face&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Asap&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&#39;/fonts/Asap-VariableFont_wght.woff2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;woff2 supports variations&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&#39;/fonts/Asap-VariableFont_wght.woff2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;woff2-variations&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 400 700&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;font-display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; swap&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;font-style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; normal&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So that&#39;s it. That&#39;s all there is to it to implement self-hosted variable fonts to your website.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Creating a feed in Eleventy</title>
    <link href="https://bnijenhuis.nl/notes/creating-a-feed-in-eleventy/"/>
    <updated>2021-04-07T00:00:00Z</updated>
    <id>https://bnijenhuis.nl/notes/creating-a-feed-in-eleventy/</id>
    <content type="html">&lt;p&gt;With the rise of all the different social media platforms several years ago, &lt;abbr title=&quot;Really Simple Syndication&quot;&gt;RSS&lt;/abbr&gt; was deemed unnecesary. Even Google &lt;a href=&quot;https://googleblog.blogspot.com/2013/03/a-second-spring-of-cleaning.html&quot;&gt;discontinued Google Reader&lt;/a&gt; in 2013. But with the weird algorithms used in social media, lots of people (well, lots of developers at least) still rely on feeds to have a chronological feed of articles they&#39;re interested in.&lt;/p&gt;
&lt;p&gt;Eleventy provides a &lt;a href=&quot;https://www.11ty.dev/docs/plugins/rss/&quot;&gt;RSS plugin&lt;/a&gt; to generate a feed for your Eleventy project. What the RSS plugin &lt;em&gt;actually&lt;/em&gt; does is create an Atom feed instead of a RSS feed, but they are basically the same. A quick search led me to &lt;a href=&quot;https://danielmiessler.com/blog/atom-rss-why-we-should-just-call-them-feeds-instead-of-rss-feeds/&quot;&gt;an article by Daniel Miessler&lt;/a&gt; about the differences between RSS and Atom and why Atom is actually the preferred method. He makes some solid points, and because it agreed with the simplest way for me to add a feed to my website, I chose to listen to him :).&lt;/p&gt;
&lt;p&gt;For a minute I considered to create a feed myself without using a plugin, but when I looked at the code of the plugin it was pretty clean already. It already handles things like making sure that urls are absolute and getting the last updated date. It made no sense to do this myself so I followed the steps described on the RSS plugin page to implement a feed on my website.&lt;/p&gt;
&lt;p&gt;One thing I did change though, is the order in which the entries are listed in the feed. In the example given on the plugin page the entries will be sorted from old to new, but I think it makes more sense to have the newest entry first. To do this, I only needed to add &lt;code&gt;|reverse&lt;/code&gt; to the &lt;code&gt;for&lt;/code&gt; declaration like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{%- for note in collections.notes|reverse %}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So now you&#39;re able to &lt;a href=&quot;https://bnijenhuis.nl/feed.xml&quot;&gt;subscribe to my notes&lt;/a&gt; using your favorite feed reader!&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Adding a favicon in Eleventy</title>
    <link href="https://bnijenhuis.nl/notes/adding-a-favicon-in-eleventy/"/>
    <updated>2021-03-25T00:00:00Z</updated>
    <id>https://bnijenhuis.nl/notes/adding-a-favicon-in-eleventy/</id>
    <content type="html">&lt;p&gt;I recently stumbled upon an article posted on CSS-Tricks about &lt;a href=&quot;https://css-tricks.com/how-to-favicon-in-2021/&quot;&gt;How to Favicon in 2021&lt;/a&gt;. Usually I would just use whatever &lt;a href=&quot;https://realfavicongenerator.net/&quot;&gt;a Favicon Generator&lt;/a&gt; would output for me, but it always felt like it returned...well...&lt;em&gt;a lot&lt;/em&gt;!&lt;/p&gt;
&lt;p&gt;That&#39;s great though, because you can serve a favicon on every browser and in every occasion. But, as &lt;a href=&quot;https://evilmartians.com/chronicles/how-to-favicon-in-2021-six-files-that-fit-most-needs&quot;&gt;the article&lt;/a&gt; the CSS-Tricks article refers to states, not all of them are really needed anymore. For more information on what files are needed and how to create them just read the other two articles. There&#39;s no need to explain them here as well.&lt;/p&gt;
&lt;p&gt;After creating the correct files, I needed to add them to my Eleventy project. I wanted to have them placed in my root directory when generated, but I want them in a seperate folder in my project. That&#39;s where &lt;a href=&quot;https://www.11ty.dev/docs/copy/&quot;&gt;Passthrough File Copy&lt;/a&gt; comes into play. You can define files and/or folders to passthrough to your generated content.&lt;/p&gt;
&lt;p&gt;I&#39;ve added the following to my &lt;a href=&quot;https://github.com/bnijenhuis/bnijenhuis-nl/blob/main/.eleventy.js&quot;&gt;&lt;code&gt;.eleventy.js&lt;/code&gt; file&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Copy `img/favicon/` to `_site/`&lt;/span&gt;&lt;br&gt;eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addPassthroughCopy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string-property property&quot;&gt;&quot;img/favicon&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All it does is get the files from the &lt;code&gt;img/favicon&lt;/code&gt; folder of my project (where I placed the created favicon files) and pass them through to the root of the generated content.&lt;/p&gt;
&lt;p&gt;This is a basic implementation of this functionality. If you want to learn more implementations and variations of Passthrough File Copy you can read the docs as linked earlier.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Collection archive in Eleventy</title>
    <link href="https://bnijenhuis.nl/notes/collection-archive-in-eleventy/"/>
    <updated>2021-03-17T00:00:00Z</updated>
    <id>https://bnijenhuis.nl/notes/collection-archive-in-eleventy/</id>
    <content type="html">&lt;p&gt;When creating collections, like I am with these notes, it&#39;s expected to have an index file of all the posts. All my notes are under the &lt;code&gt;/notes/&lt;/code&gt; url path, but by default the &lt;code&gt;/notes/&lt;/code&gt; url path itself isn&#39;t available. &lt;s&gt;And creating a file in the &lt;code&gt;notes&lt;/code&gt; folder in the Eleventy project won&#39;t fix this, because this will create a new note by itself.&lt;/s&gt; &lt;a href=&quot;https://twitter.com/nhoizey/status/1372321929873518592&quot;&gt;Nicolas Hoizey pointed out on Twitter&lt;/a&gt; that it &lt;em&gt;is&lt;/em&gt; possible, more on that at the end of this note.&lt;/p&gt;
&lt;p&gt;Luckily it&#39;s pretty easy to create such an index file using the &lt;code&gt;permalink&lt;/code&gt; option in the frontmatter. This option enables you to control the location of the created file. In my case I wanted to create an index file for all my notes, so I created the file &lt;a href=&quot;https://github.com/bnijenhuis/bnijenhuis-nl/blob/main/notes-archive.njk&quot;&gt;&lt;code&gt;notes-archive.njk&lt;/code&gt;&lt;/a&gt; in the root directory of my project with the following frontmatter:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;---&lt;br&gt;layout: layout.liquid&lt;br&gt;permalink: /notes/&lt;br&gt;---&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see the &lt;code&gt;permalink&lt;/code&gt; option has the &lt;code&gt;/notes/&lt;/code&gt; value, which means the file will be created at that url path, instead of the default &lt;code&gt;/notes-archive/&lt;/code&gt; url path. That&#39;s all there is to it to create a basic collection archive or tag page. More information can be found in the &lt;a href=&quot;https://www.11ty.dev/docs/quicktips/tag-pages/&quot;&gt;Eleventy documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Update March 18, 2021&lt;/h2&gt;
&lt;p&gt;As I mentioned earlier, it has been brought to my attention that it is possible to create an index file in the &lt;code&gt;/notes/&lt;/code&gt; folder. To make this work you have to use the &lt;code&gt;eleventyExcludeFromCollections&lt;/code&gt; option in the frontmatter and set this to &lt;code&gt;true&lt;/code&gt;, like this:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;---&lt;br&gt;layout: layout.liquid&lt;br&gt;eleventyExcludeFromCollections: true&lt;br&gt;---&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The option is pretty self explanatory. If you add this to the frontmatter and set it to &lt;code&gt;true&lt;/code&gt;, it won&#39;t be added to the collection. I think this is a better solution, because the structuring of the files in your project is much more logical. So both methods work, but I would suggest using this last method.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Dates in Eleventy</title>
    <link href="https://bnijenhuis.nl/notes/dates-in-eleventy/"/>
    <updated>2021-03-09T00:00:00Z</updated>
    <id>https://bnijenhuis.nl/notes/dates-in-eleventy/</id>
    <content type="html">&lt;p&gt;When writing notes it&#39;s important to add a date to them. Even if it&#39;s just for your own use, it can be really helpful for future you to directly see how recent and relevant the note is. Because in the ever changing world of front-end development just a year can make a real difference.&lt;/p&gt;
&lt;p&gt;Seeing how many starter blogs are available for Eleventy, I thought the date support would be baked in. And to be fair, it &lt;em&gt;kinda&lt;/em&gt; is, but when you want to control the format you need to do a little extra. And when I say a little, it really is a little. A great website for Eleventy related resources is &lt;a href=&quot;https://11ty.rocks/&quot;&gt;11ty.rocks&lt;/a&gt;, and they have &lt;a href=&quot;https://11ty.rocks/eleventyjs/dates/&quot;&gt;an article about dates&lt;/a&gt; as well.&lt;/p&gt;
&lt;p&gt;I wanted to use a different formatting than described in that article, but a quick search for Luxon date formatting led to the documentation. So I created the &lt;a href=&quot;https://www.11ty.dev/docs/config/&quot;&gt;.eleventy.js file&lt;/a&gt; and created the following filters:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addFilter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;readablePostDate&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;dateObj&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; DateTime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromJSDate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dateObj&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;zone&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Europe/Amsterdam&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setLocale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;en&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toLocaleString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;DateTime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DATE_FULL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addFilter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;postDate&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;dateObj&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; DateTime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromJSDate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dateObj&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;zone&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Europe/Amsterdam&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setLocale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;en&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toISODate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The readablePostDate filter creates a date like &lt;code&gt;March 9, 2021&lt;/code&gt;, and the postDate filter creates a date like &lt;code&gt;2021-03-09&lt;/code&gt;, which is used in the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time&quot;&gt;&lt;code&gt;time&lt;/code&gt; element&lt;/a&gt; on the homepage and the note itself.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Unhurried Development</title>
    <link href="https://bnijenhuis.nl/notes/unhurried-development/"/>
    <updated>2021-03-01T00:00:00Z</updated>
    <id>https://bnijenhuis.nl/notes/unhurried-development/</id>
    <content type="html">&lt;p&gt;I recently changed hosting and instead of moving my old website, I thought it was the perfect time to build the new website I had in my mind for a while now. The only problem was, I hadn&#39;t even started on it yet. And before I would have it at a stage where I feel comfortable enough to share it, it could be weeks, months...years even!&lt;/p&gt;
&lt;p&gt;I didn&#39;t want to wait that long, so I decided to just start. Just develop the website in small steps, without any pressure. Because let&#39;s face it, there&#39;s enough stress in the world at the moment. No need to add some more to myself.&lt;/p&gt;
&lt;p&gt;I started with the thing I do best and have the most experience in: procrastinating. First I wanted to have a name for this kind of development. The first thing that came in my mind was &amp;quot;lazy&amp;quot;, but it felt a bit too negative, as did &amp;quot;slow&amp;quot;. After some searching for synonyms of these terms, I came across &amp;quot;unhurried&amp;quot;. And it feels like the perfect term to use. No hurry, no pressure, no negative connotation. Just develop what I want, when I want.&lt;/p&gt;
&lt;p&gt;So with that sorted out, there wasn&#39;t anything else I could think of to stop me of beginning the project (trust me, I really tried to find something). Time to start. My last website was just handcoded HTML, but I wanted to try a &lt;abbr title=&quot;Static Site Generator&quot;&gt;SSG&lt;/abbr&gt;. I decided to use &lt;a href=&quot;https://www.11ty.dev/&quot;&gt;Eleventy&lt;/a&gt; to create this website. It&#39;s a really fast SSG, with lots of documentation and an active community.&lt;/p&gt;
&lt;p&gt;Although there are a lot of starter projects, I will not be using one to try and keep the code as clean as possible and only include what I need. I started by following &lt;a href=&quot;https://www.filamentgroup.com/lab/build-a-blog/&quot;&gt;this blog tutorial&lt;/a&gt; by Zach Leatherman (the creator of 11ty). I added a few CSS rules to make it readable, but that&#39;s it for now. To be honest, it took me longer to write this first post than setting up the basics.&lt;/p&gt;
&lt;p&gt;The plan is to make notes on every step I make, so I can get a little better at writing as well. But as I said earlier, there is no timeframe. Just a bunch of ideas in my head that I want to work out.&lt;/p&gt;
&lt;p&gt;So that&#39;s it, the start of my Unhurried Development project. I&#39;ll keep you posted. Probably.&lt;/p&gt;
</content>
  </entry>
</feed>