<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>ayush sharma (づ｡◕‿‿◕｡)づ</title>
        <description></description>
        <link>https://www.ayush.nz/</link>
        <atom:link href="https://www.ayush.nz/feed.xml" rel="self"
            type="application/rss+xml" />
        <pubDate>Jul 3, 2024</pubDate>
        <lastBuildDate>Jul 3, 2024</lastBuildDate>
        <generator>Jekyll v4.3.2</generator>
        
        <item>
            <title>Consuming APIs responsibly</title>
            <pubDate>Nov 21, 2022</pubDate>
            <link>https://www.ayush.nz/2022/11/consuming-apis-responsibly</link>
            <guid isPermaLink="true">https://www.ayush.nz/2022/11/consuming-apis-responsibly</guid>
            
            
            <description>
                &lt;p&gt;Building the &lt;a href=&quot;https://fediverse.to&quot;&gt;Fediverse.to&lt;/a&gt; requires hitting the API endpoints of thousands of servers. Some of these servers are huge, some are tiny, and almost all of them are managed by just 1 person or a small team. Being a sysadmin myself I know how painful it is for a server to crash due to mysteriously large amounts traffic from seemingly nowhere, so I put together a small list of dos and donts to make sure my hobby project doesn’t ruin someone’s night. What best-practices and guidelines should I follow when polling a diverse mix of other people’s servers? How can I make sure that my work doesn’t cause unintended consequences?&lt;/p&gt;

&lt;h2 id=&quot;first-do-no-harm&quot;&gt;First do no harm&lt;/h2&gt;

&lt;p&gt;The first part of the etiquette is ensuring that you don’t thrash the API server. The best way of doing this is to assume it has a small amount of compute/storage/network and serves other clients more important than you. Be kind. The best course of action is taxing the server as little possible to allow it to serve other requests.&lt;/p&gt;

&lt;h3 id=&quot;network-performance&quot;&gt;Network performance&lt;/h3&gt;

&lt;p&gt;Make requests in series rather than in parallel which will let the API server save its threads/cores for more important things. This is a judgement call since it will slow down your app/client considerably but that’s not the worst thing. Your work may be important but not mission-critical.&lt;/p&gt;

&lt;p&gt;The API might also support fetching multiple records in a single request. For example, there may be a provision to supply multiple record IDs and get the array of results in one response (like GraphQL). This ensures two things: first, the API might be able to, for example, fetch the data in a single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT&lt;/code&gt; with multiple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WHERE&lt;/code&gt; conditions lowering database transaction costs. Second, it saves multiple network calls. Use the network conservatively and batch requests wherever possible although your mileage will vary.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;http://fediverse.to&quot;&gt;fediverse.to&lt;/a&gt; backend uses the requests library which supports gzip compression by &lt;a href=&quot;https://stackoverflow.com/questions/33334606/requests-module-and-compression&quot;&gt;default&lt;/a&gt;, meaning if the server supports it, there should be &lt;a href=&quot;https://sking7.github.io/articles/751663007.html&quot;&gt;fewer bytes-on-the-wire&lt;/a&gt;. The backend also caches the responses for re-use leading to fewer requests to the API server.&lt;/p&gt;

&lt;h3 id=&quot;let-the-ux-drive-performance&quot;&gt;Let the UX drive performance&lt;/h3&gt;

&lt;p&gt;If your application is still good with 24 hour old data, don’t fetch it every 5 minutes just because you can. Think about the product you’re building and what your data freshness requirements are. This can provide huge performance gains to the target API server. The API requests you &lt;em&gt;don’t&lt;/em&gt; make are as important as the ones you do.&lt;/p&gt;

&lt;h3 id=&quot;backoff-and-retry&quot;&gt;Backoff and retry&lt;/h3&gt;

&lt;p&gt;If the server is unable to comply with your request for data, wait for a generous amount of time before trying again. It’s possible the server is using a small amount of resources and immediately retrying a deadlocked request isn’t helpful. Use &lt;a href=&quot;/2017/08/retry-strategies-for-transient-failures&quot;&gt;exponential backoffs and retries&lt;/a&gt; to give the server time to recoup and recover before requesting content again.&lt;/p&gt;

&lt;h2 id=&quot;when-all-else-fails&quot;&gt;When all else fails…&lt;/h2&gt;

&lt;h3 id=&quot;stay-reachable&quot;&gt;Stay reachable&lt;/h3&gt;

&lt;p&gt;When (not if) you end up breaking someone else’s baby, make sure you’re reachable. Your website/domain should contain your contact information so you’re reachable in an emergency.&lt;/p&gt;

&lt;p&gt;Your &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent&quot;&gt;user-agent string&lt;/a&gt; should let the server admin know what technology you’re using to hit their API. The format &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User-Agent: &amp;lt;product&amp;gt; / &amp;lt;product-version&amp;gt; &amp;lt;comment&amp;gt;&lt;/code&gt; is simple and informative.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/From&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FROM&lt;/code&gt; header field&lt;/a&gt; should contain an email address where the server admin can reach you.&lt;/p&gt;

&lt;h3 id=&quot;use-a-reputable-hosting-provider&quot;&gt;Use a reputable hosting provider&lt;/h3&gt;

&lt;p&gt;Most hosting providers publicly disclose the IP address range that they use. So if your code starts misbehaving, your IP address can be traced back to your hosting provider. And if your hosting provider has a good support team, they can reach you and help you fix things. I was recently contacted by the DigitalOcean support team about some issues one of my scripts caused. Apparently my Python client was hitting too many non-existent URLs on an API server and the server admins got in touch with DigitalOcean. I identified the problem, fixed it, and everyone lived happily ever after.&lt;/p&gt;

            </description>
        </item>
        
        <item>
            <title>Create a series of posts with navigation in Jekyll</title>
            <pubDate>Feb 18, 2022</pubDate>
            <link>https://www.ayush.nz/2022/02/creating-article-series-posts-navigation-jekyll</link>
            <guid isPermaLink="true">https://www.ayush.nz/2022/02/creating-article-series-posts-navigation-jekyll</guid>
            
            
            <description>
                &lt;p&gt;Blogging about individual self-contained ideas is great. But some ideas require a more structured approach. Chaining together simple ideas into one big whole is a great journey for both the writer and the reader. Which is why I wanted to add a Series feature to this Jekyll blog. As you may have guessed already, Jekyll’s high degree of customsation makes this a breeze.&lt;/p&gt;

&lt;h2 id=&quot;goal&quot;&gt;Goal&lt;/h2&gt;
&lt;p&gt;What I want to achieve is:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Each article should list the other articles which are part of the same series.&lt;/li&gt;
  &lt;li&gt;To simplify content discovery, the home page should display all series in a category.&lt;/li&gt;
  &lt;li&gt;Moving articles into different series should be easy since they may evolve over time.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;step-1-adding-series-metadata-to-posts&quot;&gt;Step 1: Adding series metadata to posts&lt;/h2&gt;
&lt;p&gt;Given Jekyll’s high customisability there are several ways to handle a series. Either we can leverage Jekyll variables in the config to keep a list of series. Or we can use collections. Or define a Liquid list somewhere in a global template and iterate over it.&lt;/p&gt;

&lt;p&gt;The cleanest way is to keep the series along with the posts contained in that series. For example, for all the posts in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Jekyll&lt;/code&gt; series, I’ve added the following 2 variables in the post front matter:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;is_series&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;series_title&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Jekyll&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The first variable, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;is_series&lt;/code&gt;, is a simple boolean which says whether this post is part of a series or not. Booleans work great with Liquid filters and will allow me to filter only those posts which are part of a series. This will come in handy later on when we’re trying to list all the series in one go.&lt;/p&gt;

&lt;p&gt;The second variable, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;series_title&lt;/code&gt;, is the title of this series, in this case &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Jekyll&lt;/code&gt;. It’s important that posts in the same series contain the exact same title. We’ll use this title to match posts to a series. If it contains extra spaces or special characters it won’t match the series.&lt;/p&gt;

&lt;p&gt;You can &lt;a href=&quot;https://gitlab.com/ayush-sharma/ayushsharma-in/-/blob/4.0/_posts/2022-01-25-practical-light-dark-mode-jekyll-bootstrap5.md&quot;&gt;view the source-code for this here&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;step-2-adding-links-to-posts&quot;&gt;Step 2: Adding links to posts&lt;/h2&gt;
&lt;p&gt;With our series defined, we now need to show other articles in the series. Meaning if I’m seeing a post in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Jekyll&lt;/code&gt; series there should be a list of other articles in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Jekyll&lt;/code&gt; series. A series won’t make sense without this essential navigation.&lt;/p&gt;

&lt;p&gt;This blog uses the &lt;a href=&quot;https://gitlab.com/ayush-sharma/ayushsharma-in/-/blob/4.0/_layouts/post.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;posts&lt;/code&gt; layout&lt;/a&gt; to display posts. To display other posts in the same series as the currently viewed post, I’m using the code below:&lt;/p&gt;

&lt;div class=&quot;language-liquid highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;!-- Series links --&amp;gt;
&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;is_series&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
&amp;lt;h3 class=&quot;text-success p-3 pb-0&quot;&amp;gt;&lt;span class=&quot;p&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;series_title&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;upcase&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt; series&amp;lt;/h3&amp;gt;
&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;posts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;posts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;is_series&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;series_title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;series_title&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'date'&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
 
&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;posts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
 &amp;lt;p class=&quot;nav-link bullet-pointer mb-0&quot;&amp;gt;&lt;span class=&quot;p&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;&amp;lt;/p&amp;gt;
        &lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
 &amp;lt;a class=&quot;nav-link bullet-hash&quot; href=&quot;&lt;span class=&quot;p&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;&quot;&amp;gt;&lt;span class=&quot;p&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;&amp;lt;/a&amp;gt;
        &lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;endif&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;endfor&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;endif&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The logic above goes as follows:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Check if the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;is_series&lt;/code&gt; boolean of the current page is true, meaning the post is part of a series.&lt;/li&gt;
  &lt;li&gt;Fetch posts where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;is_series&lt;/code&gt; is true and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;series_title&lt;/code&gt; is the current &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;series_title&lt;/code&gt;. Sort these in ascending order of date.&lt;/li&gt;
  &lt;li&gt;Display links to other posts in the series. Or show a non-clickable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;span&lt;/code&gt; if the list item is the current post.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I’ve stripped some of the HTML for clarity but you can &lt;a href=&quot;https://gitlab.com/ayush-sharma/ayushsharma-in/-/blob/4.0/_layouts/post.html#L27&quot;&gt;view the full source-code here&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;step-3-adding-links-to-all-series-to-home-page&quot;&gt;Step 3: Adding links to all series to home page&lt;/h2&gt;
&lt;p&gt;We now  have the post pages showing links to other posts in the same series. Next, I want to add a navigation to all series under a category on my home page.&lt;/p&gt;

&lt;p&gt;For example. On the home page, the Technology section should show all series in Technology. The same for Life Stuff, Video Games, and META categories as well. This will make it easier for users to browse and read a complete series.&lt;/p&gt;

&lt;div class=&quot;language-liquid highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;!-- Series posts --&amp;gt;
&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;series&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;,&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;series_post&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;,&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;posts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;posts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Category&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;is_series&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'date'&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;posts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;unless&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;series&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ow&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;series_title&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;series&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;series&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;series_title&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;series_post&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;series_post&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;endunless&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;endfor&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;series&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
&amp;lt;div class=&quot;row m-1 row-cols-1 row-cols-md-4 g-3 align-items-center&quot;&amp;gt;
 &amp;lt;div class=&quot;col&quot;&amp;gt;
 &amp;lt;span class=&quot;h3 text-success&quot;&amp;gt;Article series →&amp;lt;/span&amp;gt;
 &amp;lt;/div&amp;gt;
    &lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;series_post&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;card-link.html&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;post.url&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;post.series_title&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;endfor&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
&amp;lt;/div&amp;gt;
&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;endif&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;endfor&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To identify all series for a particular category, I’m using the code above, which:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Initalizes two variables: one for series names and another for the first post of each series.&lt;/li&gt;
  &lt;li&gt;Fetches all posts which have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;is_series&lt;/code&gt; set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt; and which belong to the current category.&lt;/li&gt;
  &lt;li&gt;Adds the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;series_title&lt;/code&gt; to the series names array and the first post to the series post array.&lt;/li&gt;
  &lt;li&gt;Finally, it displays the name of the series which links to the first post in that series.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can &lt;a href=&quot;https://gitlab.com/ayush-sharma/ayushsharma-in/-/blob/4.0/index.md#L76&quot;&gt;find the full source code here&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Jekyll’s high degree of customisation is why I enjoy working with it so much. And why this blog’s underlying Jekyll engine has survived redesigns and refactors. Jekyll makes it easy to add dynamic logic to your otherwise static website. And while my website remains static, the logic that renders it doesn’t have to be.&lt;/p&gt;

&lt;p&gt;There are many improvements you can make to what I’ve shown you today.&lt;/p&gt;

&lt;p&gt;One improvement I’m thinking of is handling series post ordering. For example, right now the posts in a series show in ascending order of their publish date. But I’ve published several posts belonging to a series at different times. So I can add a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;series_order&lt;/code&gt; key and use it to order articles rather than their publish date. This is one of the many ways you can build your own series feature.&lt;/p&gt;

&lt;p&gt;Happy coding :)&lt;/p&gt;

            </description>
        </item>
        
        <item>
            <title>A practical guide to light and dark mode in Bootstrap 5 and Jekyll</title>
            <pubDate>Jan 25, 2022</pubDate>
            <link>https://www.ayush.nz/2022/01/practical-light-dark-mode-jekyll-bootstrap5</link>
            <guid isPermaLink="true">https://www.ayush.nz/2022/01/practical-light-dark-mode-jekyll-bootstrap5</guid>
            
            
            <description>
                &lt;p&gt;Adding a &lt;a href=&quot;/2021/10/creating-light-and-dark-themes-for-websites-correctly-using-prefers-color-scheme&quot;&gt;light and dark mode&lt;/a&gt; to my side project &lt;a href=&quot;http://www.fediverse.to&quot;&gt;www.fediverse.to&lt;/a&gt; was a fun journey. I especially loved how intuitive the entire process was. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prefers-color-scheme&lt;/code&gt; CSS property contains the user’s color scheme - light or dark. We then define SASS or CSS styles for both modes, and the browser applies the style the user wants. That’s it! The seamless flow from operating system to browser to website is a huge win for users and developers.&lt;/p&gt;

&lt;p&gt;After tinkering with &lt;a href=&quot;http://www.fediverse.to&quot;&gt;www.fediverse.to&lt;/a&gt; (and the &lt;a href=&quot;/2021/10/creating-light-and-dark-themes-for-websites-correctly-using-prefers-color-scheme&quot;&gt;resulting post blowing past 50K+ views!&lt;/a&gt;) I decided to add light and dark modes to this website as well. I began with some internet research on how to best approach this. This &lt;a href=&quot;https://github.com/twbs/bootstrap/issues/31175&quot;&gt;GitHub thread&lt;/a&gt; shows the current progress of the feature. And &lt;a href=&quot;https://vinorodrigues.github.io/bootstrap-dark-5/&quot;&gt;this in-depth POC&lt;/a&gt; demonstrates how challenging the process can be.&lt;/p&gt;

&lt;h2 id=&quot;the-challenge&quot;&gt;The challenge&lt;/h2&gt;

&lt;p&gt;The biggest challenge is that sometimes SASS and CSS don’t play well with each other.&lt;/p&gt;

&lt;p&gt;Let me explain.&lt;/p&gt;

&lt;p&gt;From my earlier post on &lt;a href=&quot;/2021/10/creating-light-and-dark-themes-for-websites-correctly-using-prefers-color-scheme&quot;&gt;light and dark themes&lt;/a&gt;, to create both styles we need to define CSS like this:&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;/* Light mode */&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;py&quot;&gt;--body-bg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#FFFFFF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;py&quot;&gt;--body-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#000000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;/* Dark mode */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefers-color-scheme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;nd&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
       &lt;span class=&quot;py&quot;&gt;--body-bg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#000000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
       &lt;span class=&quot;py&quot;&gt;--body-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#FFFFFF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is simple enough. With the styles defined, we use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;var(--body-bg)&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;var(--body-color)&lt;/code&gt; in our CSS. This causes the colors to switch based on the value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prefers-color-scheme&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Bootstrap 5 uses Sass to define color values. My website’s &lt;a href=&quot;https://gitlab.com/ayush-sharma/ayushsharma-in/-/blob/2.0/_sass/_variables.scss&quot;&gt;color scheme in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_variables.scss&lt;/code&gt;&lt;/a&gt; looks like this:&lt;/p&gt;

&lt;div class=&quot;language-scss highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// User-defined colors&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$my-link-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;#FFCCBB&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$my-text-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;#E2E8E4&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$my-bg-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;#303C6C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The solution seems obvious now, right? We can combine &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prefers-color-scheme&lt;/code&gt; with the variables above, and boom!&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;/* User-defined colors */&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;--my-link-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#FFCCBB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;--my-text-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#E2E8E4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;--my-bg-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#303C6C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;/* Dark mode */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefers-color-scheme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;py&quot;&gt;--my-link-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#FF0000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;py&quot;&gt;--my-text-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#FFFFFF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;py&quot;&gt;--my-bg-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#000000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Additionally, we need to replace the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$&lt;/code&gt; values with their &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--&lt;/code&gt; variants in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_variables.scss&lt;/code&gt;.
After making the change and running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jekyll build&lt;/code&gt;, we get the following:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Conversion error: Jekyll::Converters::Scss encountered an error &lt;span class=&quot;k&quot;&gt;while &lt;/span&gt;converting &lt;span class=&quot;s1&quot;&gt;'css/main.scss'&lt;/span&gt;:
                    Error: argument &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$color2&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt; of &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;mix&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$color1&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;$color2&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;$weight&lt;/span&gt;: 50%&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt; must be a color on line 161:11 of _sass/_functions.scss, &lt;span class=&quot;k&quot;&gt;in function&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;mix&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt; from line 161:11 of _sass/_functions.scss, &lt;span class=&quot;k&quot;&gt;in function&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;shade-color&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt; from line 166:27 of _sass/_functions.scss, &lt;span class=&quot;k&quot;&gt;in function&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt; from line 166:11 of _sass/_functions.scss, &lt;span class=&quot;k&quot;&gt;in function&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;shift-color&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt; from line 309:43 of _sass/_variables.scss from line 11:9 of _sass/bootstrap.scss from line 1:9 of stdin &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; @return mix&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;black, &lt;span class=&quot;nv&quot;&gt;$color&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;$weight&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;----------&lt;/span&gt;^ 
             Error: Error: argument &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$color2&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt; of &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;mix&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$color1&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;$color2&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;$weight&lt;/span&gt;: 50%&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt; must be a color on line 161:11 of _sass/_functions.scss, &lt;span class=&quot;k&quot;&gt;in function&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;mix&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt; from line 161:11 of _sass/_functions.scss, &lt;span class=&quot;k&quot;&gt;in function&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;shade-color&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt; from line 166:27 of _sass/_functions.scss, &lt;span class=&quot;k&quot;&gt;in function&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt; from line 166:11 of _sass/_functions.scss, &lt;span class=&quot;k&quot;&gt;in function&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;shift-color&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt; from line 309:43 of _sass/_variables.scss from line 11:9 of _sass/bootstrap.scss from line 1:9 of stdin &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; @return mix&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;black, &lt;span class=&quot;nv&quot;&gt;$color&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;$weight&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;----------&lt;/span&gt;^ 
             Error: Run jekyll build &lt;span class=&quot;nt&quot;&gt;--trace&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;more information.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The error means that the Bootstrap mixins expect color values to be, well, color values. And not CSS variables. From here we can dig down into the Bootstrap code to rewrite the mixin. But we’ll end up rewriting most of Bootstrap to get this to work. &lt;a href=&quot;https://vinorodrigues.github.io/bootstrap-dark/readme.html&quot;&gt;This page&lt;/a&gt; describes most of the options available to us at this point. But I was able to make-do with a simpler approach.&lt;/p&gt;

&lt;p&gt;Since I don’t use the entire suite of Bootstrap features, I was able to add light and dark mode with a combination of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prefers-color-scheme&lt;/code&gt;, some CSS overrides, and a little bit of code duplication.&lt;/p&gt;

&lt;h2 id=&quot;step-1-separate-presentation-from-structure&quot;&gt;Step 1: Separate presentation from structure&lt;/h2&gt;

&lt;p&gt;Before applying the new styles to handle light and dark mode there was some clean up to perform on the HTML and CSS.&lt;/p&gt;

&lt;p&gt;The first step is ensuring that all the presentation layer stuff is in the CSS and not the HTML. The presentation markup (CSS) should always stay separate from the page structure (HTML). But a website’s source code can get messy with time. If your color classes are already separated into the CSS you can skip this step.&lt;/p&gt;

&lt;p&gt;I found my HTML code peppered with Bootstrap color classes. Certain &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;div&lt;/code&gt;s and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;footer&lt;/code&gt;s were using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;text-light&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;text-dark&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bg-light&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bg-dark&lt;/code&gt; within the HTML. Since handling the light and dark theme relies on CSS, the color classes had to go. So &lt;a href=&quot;https://gitlab.com/ayush-sharma/ayushsharma-in/-/commit/97a5250e9ab351dfd1e1aa7374344166efc14a35&quot;&gt;I moved them all from the HTML into my custom SASS file&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I left the contextual color classes (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bg-primary&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bg-warning&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;text-muted&lt;/code&gt;, etc.) as-is. The colors I’ve picked for my light and dark themes would not interfere with them. Make sure your theme colors work well with contextual colors. Otherwise you should move them into the CSS as well.&lt;/p&gt;

&lt;p&gt;So far I’ve written 100+ articles on this site. So I had to scan all my posts under the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_posts/&lt;/code&gt; directory hunting down color classes. Like the step above, make sure to move all color classes into the CSS. Don’t forget to check the Jekyll &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;collections&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pages&lt;/code&gt; as well.&lt;/p&gt;

&lt;h2 id=&quot;step-2-consolidate-styles-wherever-possible&quot;&gt;Step 2: Consolidate styles wherever possible&lt;/h2&gt;

&lt;p&gt;Consolidating and reusing styling elements will ensure we have less to worry about.
My &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Projects&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Featured Writing&lt;/code&gt; sections on the home page were displaying card-like layouts. These were using custom CSS styling of their own. I &lt;a href=&quot;https://gitlab.com/ayush-sharma/ayushsharma-in/-/commit/8476b2af93028284474819df81c8dc843b81e9f4&quot;&gt;restyled them to match the article links&lt;/a&gt; and now I have less to worry about.&lt;/p&gt;

&lt;p&gt;There were several other elements using styles of their own. Instead of restyling them, I chose to remove them.&lt;/p&gt;

&lt;p&gt;The footer, for example, was using its own background color. This would have required two different colors for light and dark theme. I chose to remove the background from the footer to make the migration easier. The &lt;a href=&quot;https://gitlab.com/ayush-sharma/ayushsharma-in/-/commit/c87bf03d54989b165faaf0083e893027b26e7275&quot;&gt;footer now takes the color of the background&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If your website uses too many styles it might be prudent to remove them for the migration. After the move to light/dark themes is complete you can add them back.&lt;/p&gt;

&lt;p&gt;The goal is that we want to keep the migration simple and add new styles later if required.&lt;/p&gt;

&lt;h2 id=&quot;step-3-add-the-light-and-dark-color-schemes&quot;&gt;Step 3: Add the light and dark color schemes&lt;/h2&gt;

&lt;p&gt;With the clean up complete we can now focus on adding the styling elements for light and dark themes. We define the new color styles and apply them to the HTML elements. I chose to start with the following:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--body-bg&lt;/code&gt; for the background color.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--body-color&lt;/code&gt; for the main body/text color.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--body-link-color&lt;/code&gt; for the links.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--card-bg&lt;/code&gt; for the Bootstrap Card background colors.&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;/* Light mode */&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;--body-bg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#EEE2DC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;--body-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#AC3B61&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;--body-link-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#AC3B61&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;--card-bg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#EDC7B7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;/* Dark mode */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefers-color-scheme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;py&quot;&gt;--body-bg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#303C6C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;py&quot;&gt;--body-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#E2E8E4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;py&quot;&gt;--body-link-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#FFCCBB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;py&quot;&gt;--card-bg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#212529&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With the colors defined I changed the CSS to use the new colors. For example, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;body&lt;/code&gt; element now looks like this:&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;--body-bg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;--body-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;!important&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You can &lt;a href=&quot;https://gitlab.com/ayush-sharma/ayushsharma-in/-/commit/493746995a2863674a65644122bd5991c3ef8845?view=parallel&quot;&gt;view the rest of the CSS changes on GitLab&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can override Bootstrap 5 defaults if it’s compiled with your Jekyll source and not from the CDN. This might make sense to simplify the custom styling you need to handle. For example, &lt;a href=&quot;https://gitlab.com/ayush-sharma/ayushsharma-in/-/commit/af3e07a9a03ff33e4a6874f1ea505b7674a4077d&quot;&gt;turning off link decoration&lt;/a&gt; made life a little easier for me.&lt;/p&gt;

&lt;div class=&quot;language-scss highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$link-hover-decoration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;none&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;step-4-the-navbar-toggler&quot;&gt;Step 4: The Navbar Toggler&lt;/h2&gt;

&lt;p&gt;Last but not the least: the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;navbar&lt;/code&gt; toggler. In Bootstrap 5, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;navbar-light&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;navbar-dark&lt;/code&gt; control the color of the toggler. These are defined in the main &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nav&lt;/code&gt; element along with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.navbar&lt;/code&gt;. Since we’re not hard-coding color classes in the HTML anymore, we’ll need to duplicate the CSS in our own. So I &lt;a href=&quot;https://gitlab.com/ayush-sharma/ayushsharma-in/-/commit/ff72a24f0ad83e75bbe1eca6d8ab663bb0415d39&quot;&gt;extended the default Sass and added my theme colors&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-scss highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;.navbar-toggler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;@extend&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.navbar-toggler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;border-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;.navbar-toggler-icon&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;@extend&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.navbar-toggler-icon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;escape-svg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;url(&quot;data:image/svg+xml,&amp;lt;svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'&amp;gt;&amp;lt;path stroke='#000000' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/&amp;gt;&amp;lt;/svg&amp;gt;&quot;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The code above is the default Bootstrap 5 toggler CSS code, with some minor changes. One thing to note here. For the toggler icon I’m hardcoding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stroke='#000000'&lt;/code&gt; since black works with my theme colors. You may need to be more creative about picking colors schemes that work well across the board.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;https://www.ayush.nz/static/images/img-normal/2022-01-25-practical-light-dark-mode-jekyll-bootstrap5-mobile-both.jpg&quot; width=&quot;1024&quot; alt=&quot;Image showing www.ayushsharma.in in dark mode on the left and in light mode on the right.&quot; /&gt;
    &lt;figcaption&gt;&lt;a href=&quot;https://www.ayush.nz/static/images/img-normal/2022-01-25-practical-light-dark-mode-jekyll-bootstrap5-mobile-both.jpg&quot; target=&quot;_blank&quot;&gt;&lt;i class=&quot;fas fa-search-plus me-1&quot;&gt;&lt;/i&gt;Enlarge&lt;/a&gt; — Image showing www.ayushsharma.in in dark mode on the left and in light mode on the right.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;And that’s about it! The light and dark modes now work as expected!&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;https://www.ayush.nz/static/images/img-normal/2022-01-25-practical-light-dark-mode-jekyll-bootstrap5-desktop-dark.jpg&quot; width=&quot;1024&quot; alt=&quot;Image showing www.ayushsharma.in in dark mode in desktop view.&quot; /&gt;
    &lt;figcaption&gt;&lt;a href=&quot;https://www.ayush.nz/static/images/img-normal/2022-01-25-practical-light-dark-mode-jekyll-bootstrap5-desktop-dark.jpg&quot; target=&quot;_blank&quot;&gt;&lt;i class=&quot;fas fa-search-plus me-1&quot;&gt;&lt;/i&gt;Enlarge&lt;/a&gt; — Image showing www.ayushsharma.in in dark mode in desktop view.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
    &lt;img src=&quot;https://www.ayush.nz/static/images/img-normal/2022-01-25-practical-light-dark-mode-jekyll-bootstrap5-desktop-light.jpg&quot; width=&quot;1024&quot; alt=&quot;Image showing www.ayushsharma.in in light mode in desktop view.&quot; /&gt;
    &lt;figcaption&gt;&lt;a href=&quot;https://www.ayush.nz/static/images/img-normal/2022-01-25-practical-light-dark-mode-jekyll-bootstrap5-desktop-light.jpg&quot; target=&quot;_blank&quot;&gt;&lt;i class=&quot;fas fa-search-plus me-1&quot;&gt;&lt;/i&gt;Enlarge&lt;/a&gt; — Image showing www.ayushsharma.in in light mode in desktop view.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
    &lt;img src=&quot;https://www.ayush.nz/static/images/img-normal/2022-01-25-practical-light-dark-mode-jekyll-bootstrap5-mobile-dark.jpg&quot; width=&quot;1024&quot; alt=&quot;Image showing www.ayushsharma.in in dark mode in mobile view.&quot; /&gt;
    &lt;figcaption&gt;&lt;a href=&quot;https://www.ayush.nz/static/images/img-normal/2022-01-25-practical-light-dark-mode-jekyll-bootstrap5-mobile-dark.jpg&quot; target=&quot;_blank&quot;&gt;&lt;i class=&quot;fas fa-search-plus me-1&quot;&gt;&lt;/i&gt;Enlarge&lt;/a&gt; — Image showing www.ayushsharma.in in dark mode in mobile view.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
    &lt;img src=&quot;https://www.ayush.nz/static/images/img-normal/2022-01-25-practical-light-dark-mode-jekyll-bootstrap5-mobile-light.jpg&quot; width=&quot;1024&quot; alt=&quot;Image showing www.ayushsharma.in in light mode in mobile view.&quot; /&gt;
    &lt;figcaption&gt;&lt;a href=&quot;https://www.ayush.nz/static/images/img-normal/2022-01-25-practical-light-dark-mode-jekyll-bootstrap5-mobile-light.jpg&quot; target=&quot;_blank&quot;&gt;&lt;i class=&quot;fas fa-search-plus me-1&quot;&gt;&lt;/i&gt;Enlarge&lt;/a&gt; — Image showing www.ayushsharma.in in light mode in mobile view.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Bootstrap 5 is complex to say the least. There is lots to think about when overriding it with your custom styling. Providing a light and dark variant for every Bootstrap 5 component is difficult. But it’s possible if you don’t have too many components to deal with.&lt;/p&gt;

&lt;p&gt;By ensuring that the markup stays in Sass/CSS, reusing styles, and overriding some Bootstrap 5 defaults, it’s possible to achieve light and dark modes. It’s not a comprehensive approach but it is practical. And serviceable until Bootstrap 5 decides to provide this feature out of the box.&lt;/p&gt;

&lt;p&gt;I hope this gives you more practical ideas on how to add light and dark themes for your own website. If you find a better way using your own CSS magic, don’t forget to share it with the community.&lt;/p&gt;

&lt;p&gt;Happy coding :)&lt;/p&gt;

            </description>
        </item>
        
        <item>
            <title>Nemo - The Ubuntu file manager you didn’t know you needed</title>
            <pubDate>Jan 25, 2022</pubDate>
            <link>https://www.ayush.nz/2022/01/nemo-file-manager-ubuntu-20.04-linux-nautilus-alternative</link>
            <guid isPermaLink="true">https://www.ayush.nz/2022/01/nemo-file-manager-ubuntu-20.04-linux-nautilus-alternative</guid>
            
            
            <description>
                &lt;p&gt;So… I finally got bored with Nautilus.&lt;/p&gt;

&lt;p&gt;Ubuntu has been my daily driver for the last several years. I remember when Linux used to be a second-class citizen in the desktop space, only used by nerds and devs who could unravel its arcane and obscure mysteries. But it has come a long way. Not only is it a lot more user-friendly with a plethora of customisation options, but Steam, and especially Proton, has added first-class gaming support for Linux. I haven’t had a dual boot Ubuntu/Windows system in quite a while and that’s a huge win.&lt;/p&gt;

&lt;p&gt;Nautilus seems out of place in this brave new world, like it’s still stuck in the old way of doing things. It’s become more stable, sure, but it doesn’t match up to Windows’s Explorer or Mac’s Finder. So a few days ago I just searched for “&lt;a href=&quot;https://duckduckgo.com/?q=best+ubuntu+file+manager+2022&quot;&gt;best ubuntu file manager 2022&lt;/a&gt;”… and that’s how I found Nemo.&lt;/p&gt;

&lt;p&gt;Nemo (&lt;a href=&quot;https://en.wikipedia.org/wiki/Nemo_(file_manager)&quot;&gt;wiki&lt;/a&gt;, &lt;a href=&quot;https://github.com/linuxmint/nemo&quot;&gt;GitHub&lt;/a&gt;) is the file manager for the Cinnamon desktop environment. It’s beautiful, snappy, and useful. It’s beautiful, snappy, and, erm, useful. Compared to Nautilus, browsing remote networks is faster and the feature-set more comprehensive.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;https://www.ayush.nz/static/images/img-normal/2022-01-25-nemo-file-manager-ubuntu-20.04-linux-nautilus-alternative-comparison.jpg&quot; width=&quot;1024&quot; alt=&quot;Image showing Nautilus file manager on the left and Nemo file manager on the right. Using Ubuntu 20.04 LTS.&quot; /&gt;
    &lt;figcaption&gt;&lt;a href=&quot;https://www.ayush.nz/static/images/img-normal/2022-01-25-nemo-file-manager-ubuntu-20.04-linux-nautilus-alternative-comparison.jpg&quot; target=&quot;_blank&quot;&gt;&lt;i class=&quot;fas fa-search-plus me-1&quot;&gt;&lt;/i&gt;Enlarge&lt;/a&gt; — Image showing Nautilus file manager on the left and Nemo file manager on the right. Using Ubuntu 20.04 LTS.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;feature-list&quot;&gt;Feature list&lt;/h2&gt;
&lt;p&gt;These are some of the features of Nemo that I found most useful:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Type-ahead search.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://askubuntu.com/questions/1095891/nautilus-menu-bar-missing-in-ubuntu-18-04&quot;&gt;Menu bar&lt;/a&gt;! Plus customizable toolbar, customizable tooltip, and customizable date format.&lt;/li&gt;
  &lt;li&gt;You can open a separate panel to browse files, besides new tabs and windows.&lt;/li&gt;
  &lt;li&gt;The sidebar has a tree view! Also shows filesystem free space graphically.&lt;/li&gt;
  &lt;li&gt;Copying, moving, or extracting files shows progress in a separate mini-window (like Mac). A queue manages all file operations.&lt;/li&gt;
  &lt;li&gt;Advanced bookmarks editor - like the one in Firefox.&lt;/li&gt;
  &lt;li&gt;Separate view settings for separate folders.&lt;/li&gt;
  &lt;li&gt;The status bar shows number of files and available disk space.&lt;/li&gt;
  &lt;li&gt;File renames are in-place (like Mac) rather than in a popup.&lt;/li&gt;
  &lt;li&gt;Trash customisation - bypass Trash on delete or add a separate button to bypass Trash.&lt;/li&gt;
  &lt;li&gt;Built-in bulk-rename (though I haven’t used this one yet).&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/linuxmint/nemo-extensions&quot;&gt;Extensions&lt;/a&gt;!&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;installing-nemo&quot;&gt;Installing Nemo&lt;/h2&gt;

&lt;p&gt;Installing Nemo is about as easy as you would expect. Do this:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;nemo
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And voila! You should now have another “Files” app in your Applications Menu. Keep Nautilus around till you’re comfortable with Nemo, and then you can nuke Nautilus for good.&lt;/p&gt;

&lt;h2 id=&quot;set-nemo-as-the-default-file-manager&quot;&gt;Set Nemo as the default file manager&lt;/h2&gt;

&lt;p&gt;To set Nemo as the default file manager, do this:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;xdg-mime default nemo.desktop inode/directory application/x-gnome-saved-search
gsettings &lt;span class=&quot;nb&quot;&gt;set &lt;/span&gt;org.gnome.desktop.background show-desktop-icons &lt;span class=&quot;nb&quot;&gt;false
&lt;/span&gt;gsettings &lt;span class=&quot;nb&quot;&gt;set &lt;/span&gt;org.nemo.desktop show-desktop-icons &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;screenshots&quot;&gt;Screenshots&lt;/h2&gt;

&lt;figure&gt;
    &lt;img src=&quot;https://www.ayush.nz/static/images/img-normal/2022-01-25-nemo-file-manager-ubuntu-20.04-linux-nautilus-alternative-view-menu.jpg&quot; width=&quot;1024&quot; alt=&quot;Image showing Nemo file manager on Ubuntu 20.04 LTS with View menu open.&quot; /&gt;
    &lt;figcaption&gt;&lt;a href=&quot;https://www.ayush.nz/static/images/img-normal/2022-01-25-nemo-file-manager-ubuntu-20.04-linux-nautilus-alternative-view-menu.jpg&quot; target=&quot;_blank&quot;&gt;&lt;i class=&quot;fas fa-search-plus me-1&quot;&gt;&lt;/i&gt;Enlarge&lt;/a&gt; — Image showing Nemo file manager on Ubuntu 20.04 LTS with View menu open.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
    &lt;img src=&quot;https://www.ayush.nz/static/images/img-normal/2022-01-25-nemo-file-manager-ubuntu-20.04-linux-nautilus-alternative-bookmarks-menu.jpg&quot; width=&quot;1024&quot; alt=&quot;Image showing Nemo file manager on Ubuntu 20.04 LTS with Bookmarks menu open.&quot; /&gt;
    &lt;figcaption&gt;&lt;a href=&quot;https://www.ayush.nz/static/images/img-normal/2022-01-25-nemo-file-manager-ubuntu-20.04-linux-nautilus-alternative-bookmarks-menu.jpg&quot; target=&quot;_blank&quot;&gt;&lt;i class=&quot;fas fa-search-plus me-1&quot;&gt;&lt;/i&gt;Enlarge&lt;/a&gt; — Image showing Nemo file manager on Ubuntu 20.04 LTS with Bookmarks menu open.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
    &lt;img src=&quot;https://www.ayush.nz/static/images/img-normal/2022-01-25-nemo-file-manager-ubuntu-20.04-linux-nautilus-alternative-views-preferences.jpg&quot; width=&quot;1024&quot; alt=&quot;Image showing Nemo file manager on Ubuntu 20.04 LTS with View preferences open.&quot; /&gt;
    &lt;figcaption&gt;&lt;a href=&quot;https://www.ayush.nz/static/images/img-normal/2022-01-25-nemo-file-manager-ubuntu-20.04-linux-nautilus-alternative-views-preferences.jpg&quot; target=&quot;_blank&quot;&gt;&lt;i class=&quot;fas fa-search-plus me-1&quot;&gt;&lt;/i&gt;Enlarge&lt;/a&gt; — Image showing Nemo file manager on Ubuntu 20.04 LTS with View preferences open.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
    &lt;img src=&quot;https://www.ayush.nz/static/images/img-normal/2022-01-25-nemo-file-manager-ubuntu-20.04-linux-nautilus-alternative-behavior-preferences.jpg&quot; width=&quot;1024&quot; alt=&quot;Image showing Nemo file manager on Ubuntu 20.04 LTS with Behavior preferences open.&quot; /&gt;
    &lt;figcaption&gt;&lt;a href=&quot;https://www.ayush.nz/static/images/img-normal/2022-01-25-nemo-file-manager-ubuntu-20.04-linux-nautilus-alternative-behavior-preferences.jpg&quot; target=&quot;_blank&quot;&gt;&lt;i class=&quot;fas fa-search-plus me-1&quot;&gt;&lt;/i&gt;Enlarge&lt;/a&gt; — Image showing Nemo file manager on Ubuntu 20.04 LTS with Behavior preferences open.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
    &lt;img src=&quot;https://www.ayush.nz/static/images/img-normal/2022-01-25-nemo-file-manager-ubuntu-20.04-linux-nautilus-alternative-display-preferences.jpg&quot; width=&quot;1024&quot; alt=&quot;Image showing Nemo file manager on Ubuntu 20.04 LTS with Display preferences open.&quot; /&gt;
    &lt;figcaption&gt;&lt;a href=&quot;https://www.ayush.nz/static/images/img-normal/2022-01-25-nemo-file-manager-ubuntu-20.04-linux-nautilus-alternative-display-preferences.jpg&quot; target=&quot;_blank&quot;&gt;&lt;i class=&quot;fas fa-search-plus me-1&quot;&gt;&lt;/i&gt;Enlarge&lt;/a&gt; — Image showing Nemo file manager on Ubuntu 20.04 LTS with Display preferences open.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
    &lt;img src=&quot;https://www.ayush.nz/static/images/img-normal/2022-01-25-nemo-file-manager-ubuntu-20.04-linux-nautilus-alternative-toolbar-preferences.jpg&quot; width=&quot;1024&quot; alt=&quot;Image showing Nemo file manager on Ubuntu 20.04 LTS with Toolbar preferences open.&quot; /&gt;
    &lt;figcaption&gt;&lt;a href=&quot;https://www.ayush.nz/static/images/img-normal/2022-01-25-nemo-file-manager-ubuntu-20.04-linux-nautilus-alternative-toolbar-preferences.jpg&quot; target=&quot;_blank&quot;&gt;&lt;i class=&quot;fas fa-search-plus me-1&quot;&gt;&lt;/i&gt;Enlarge&lt;/a&gt; — Image showing Nemo file manager on Ubuntu 20.04 LTS with Toolbar preferences open.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
    &lt;img src=&quot;https://www.ayush.nz/static/images/img-normal/2022-01-25-nemo-file-manager-ubuntu-20.04-linux-nautilus-alternative-context-menu-preferences.jpg&quot; width=&quot;1024&quot; alt=&quot;Image showing Nemo file manager on Ubuntu 20.04 LTS with Context Menu preferences open.&quot; /&gt;
    &lt;figcaption&gt;&lt;a href=&quot;https://www.ayush.nz/static/images/img-normal/2022-01-25-nemo-file-manager-ubuntu-20.04-linux-nautilus-alternative-context-menu-preferences.jpg&quot; target=&quot;_blank&quot;&gt;&lt;i class=&quot;fas fa-search-plus me-1&quot;&gt;&lt;/i&gt;Enlarge&lt;/a&gt; — Image showing Nemo file manager on Ubuntu 20.04 LTS with Context Menu preferences open.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
    &lt;img src=&quot;https://www.ayush.nz/static/images/img-normal/2022-01-25-nemo-file-manager-ubuntu-20.04-linux-nautilus-alternative-plugins-preferences.jpg&quot; width=&quot;1024&quot; alt=&quot;Image showing Nemo file manager on Ubuntu 20.04 LTS with Plugins preferences open.&quot; /&gt;
    &lt;figcaption&gt;&lt;a href=&quot;https://www.ayush.nz/static/images/img-normal/2022-01-25-nemo-file-manager-ubuntu-20.04-linux-nautilus-alternative-plugins-preferences.jpg&quot; target=&quot;_blank&quot;&gt;&lt;i class=&quot;fas fa-search-plus me-1&quot;&gt;&lt;/i&gt;Enlarge&lt;/a&gt; — Image showing Nemo file manager on Ubuntu 20.04 LTS with Plugins preferences open.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Enjoy your new Ubuntu :)&lt;/p&gt;

            </description>
        </item>
        
        <item>
            <title>Announcing Fediverse.to!</title>
            <pubDate>Jan 1, 2022</pubDate>
            <link>https://www.ayush.nz/2022/01/announcing-fediverse-to</link>
            <guid isPermaLink="true">https://www.ayush.nz/2022/01/announcing-fediverse-to</guid>
            
            
            <description>
                &lt;p&gt;Over the last few weeks, I’ve been working on a search engine for Fediverse instances!&lt;/p&gt;

&lt;p&gt;The idea was to help new users, Facebook refugees, and Twitter rage-quitters find their ideal home on the fediverse. Fediverse instances are either for a wide audience or centered around a theme, a language, a hobby, or all of the above. The fediverse ecosystem has been growing like crazy - there are now close to 5000 instances with 4 million users. And finding an instance can be quite daunting.&lt;/p&gt;

&lt;p&gt;While instances.social already has a &lt;a href=&quot;https://instances.social/list#lang=&amp;amp;allowed=&amp;amp;prohibited=&amp;amp;min-users=&amp;amp;max-users=&quot;&gt;great API&lt;/a&gt; and filtering mechanism, I wanted to build something more visual. I began by polling the original API to build a visual search feature using Lunr.js, but it has now become something bigger, thanks to feedback and support of the community.&lt;/p&gt;

&lt;h2 id=&quot;fediverseto&quot;&gt;Fediverse.to!&lt;/h2&gt;

&lt;p&gt;If you visit &lt;a href=&quot;https://www.fediverse.to&quot;&gt;fediverse.to&lt;/a&gt;, you’ll see the new face of what was originally findmymastodon.com.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The branding has changed - the site uses &lt;a href=&quot;https://en.wikipedia.org/wiki/Fediverse&quot;&gt;the fediverse logo&lt;/a&gt; (link to &lt;a href=&quot;https://socialhub.activitypub.rocks/t/fediverse-logo-or-icon-proposal/1057&quot;&gt;proposal&lt;/a&gt;).&lt;/li&gt;
  &lt;li&gt;The home page now has a larger selection of instances for new users to choose from, with dedicated sections for some languages and categories.&lt;/li&gt;
  &lt;li&gt;Every instance now has its own dedicated page which, at the moment, shows basic statistics about that instance.&lt;/li&gt;
  &lt;li&gt;The home page shows newest instances at the top to support new members of the federation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Apart from the above, the original features are still there:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Support for light and dark theme.&lt;/li&gt;
  &lt;li&gt;Display total users, total status updates, and weekly active users for all instances.&lt;/li&gt;
  &lt;li&gt;Display description, language, categories, and prohibited content settings for all instances.&lt;/li&gt;
  &lt;li&gt;The search features allows searching for specific text in instance names and descriptions.&lt;/li&gt;
  &lt;li&gt;Filtering by language, category, content blocks, minimum users, and maximum users is supported.&lt;/li&gt;
  &lt;li&gt;The website visitor stats are public thanks to Plausible.&lt;/li&gt;
  &lt;li&gt;The website is still an open-source static website deployed using Netlify.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;a-new-engine-under-the-hood&quot;&gt;A new engine under the hood&lt;/h2&gt;

&lt;p&gt;Under the hood, though, is where the real changes have happened.&lt;/p&gt;

&lt;p&gt;Originally, I was using a small Python script to scrape the main API and generate a Lunr search index. The front-end website would then use JavaScript, Bootstrap, and Lunr to display visual cards for search results. There was also a basic filtering mechanism to mirror the original instances.social API which still exists.&lt;/p&gt;

&lt;p&gt;Now &lt;a href=&quot;https://www.fediverse.to&quot;&gt;fediverse.to&lt;/a&gt; uses a Django + PostgreSQL back-end to dynamically generate Jekyll pages for each instance.&lt;/p&gt;

&lt;p&gt;First, the API is used to fetch a list of instances. Then Django visits each instance’s metadata URL and pulls its information and the list of domains it peers with. The process is then repeated for all the peers. This way I can be certain I’m not missing any instance in the network. Once this process completes, Django generates a Jekyll page for each instance.&lt;/p&gt;

&lt;p&gt;For example, if you browse &lt;a href=&quot;https://gitlab.com/ayush-sharma/to-the-fediverse/-/blob/master/_posts/2021-11-28-mastodon-social.md&quot;&gt;Mastodon.social’s Jekyll page&lt;/a&gt;, you’ll see the following:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;layout&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;instance&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mastodon.social&quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;instance_url&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mastodon.social&quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;short_description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;by&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;developers&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;not&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;focused&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;particular&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;niche&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;interest&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;everyone&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;welcome&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;you&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;follow&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;our&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;conduct!...&quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;by&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;developers&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;not&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;focused&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;particular&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;niche&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;interest&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;everyone&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;welcome&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;you&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;follow&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;our&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;conduct!&quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;admin&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;staff@mastodon.social&quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;admin_email&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;staff@mastodon.social&quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;statuses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;32950135&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;active_users&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;599995&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;599995&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;statuses_display&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;33M&quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;active_users_display&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;600k&quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;users_display&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;600k&quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;languages&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;en&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;categories&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; 
&lt;span class=&quot;na&quot;&gt;thumbnail&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;files.mastodon.social/site_uploads/files/000/000/001/original/vlcsnap-2018-08-27-16h43m11s127.png&quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;registrations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;True&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;approval_required&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;False&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;3.4.3&quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;created_at&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1638080432.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;future-plans&quot;&gt;Future plans&lt;/h2&gt;

&lt;p&gt;The idea behind generating a separate page for each instance is simple - it allows the site to grow! I’m planning to add the following features soon:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Use D3 to show the entire fediverse network using network/peer visualizations.&lt;/li&gt;
  &lt;li&gt;Add more information for each instance, such as the fediverse software its running (Mastodon/Misskey/etc) and the country its hosted in.&lt;/li&gt;
  &lt;li&gt;Parse the instance domain for country information, such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.de&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.fr&lt;/code&gt;, and categories, such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.tech&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.art&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Parse the instance descriptions for country and category information.&lt;/li&gt;
  &lt;li&gt;Build an API for other users and admins to scrape.&lt;/li&gt;
  &lt;li&gt;Host everything on a remote server and automate content updates using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cronjob&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;django-crons&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Eventually I’m hoping &lt;a href=&quot;https://www.fediverse.to&quot;&gt;fediverse.to&lt;/a&gt; can become its own data source and the de-facto directory for the new internet - but that’s still a long way off :)&lt;/p&gt;

&lt;h2 id=&quot;work-to-be-done&quot;&gt;Work to be done&lt;/h2&gt;

&lt;p&gt;Aside from new features, there’s still work to be done to make &lt;a href=&quot;https://www.fediverse.to&quot;&gt;fediverse.to&lt;/a&gt; fully functional.&lt;/p&gt;

&lt;p&gt;The search feature currently has some bugs to iron out. I’m still working on the new back-end to generate the Lunr search index. I debated whether or not to release the site in its current state, but the earlier I get feedback the better the site will be. I’m hoping all of you will bear with me just a little while longer.&lt;/p&gt;

&lt;h2 id=&quot;stay-tuned&quot;&gt;Stay tuned!&lt;/h2&gt;

&lt;p&gt;Anyhoo. That’s the general plan. &lt;a href=&quot;https://www.fediverse.to&quot;&gt;fediverse.to&lt;/a&gt; is my first open-source project so I’m learning as I go along. But you’ve all been very supportive with ideas and feedback and I’m already excited about version 2.0!&lt;/p&gt;

&lt;p&gt;So wish me luck… and stay tuned for more! I’ll post future updates here, so follow me using the link on the, erm, &lt;a href=&quot;https://www.buymeacoffee.com/ayushsharma&quot;&gt;top right&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;And before I forget… spread the word!&lt;/p&gt;

&lt;p&gt;Thanks,
Ayush.&lt;/p&gt;

            </description>
        </item>
        
        <item>
            <title>Easy pretty URL redirects with Jekyll and Netlify</title>
            <pubDate>Nov 16, 2021</pubDate>
            <link>https://www.ayush.nz/2021/11/easy-pretty-url-redirects-with-jekyll-and-netlify</link>
            <guid isPermaLink="true">https://www.ayush.nz/2021/11/easy-pretty-url-redirects-with-jekyll-and-netlify</guid>
            
            
            <description>
                &lt;p&gt;Pretty URLs, aka vanity URLs, aka tiny URLs, are great for improving SEO and the UX. They’re shorter versions of a domain, making them useful for copy-pasting and printing. There are already plenty of domain shortening services out there. But with Jekyll and Netlify I can configure pretty URLs all by my lonesome!&lt;/p&gt;

&lt;p&gt;What I want to achieve is as follows.&lt;/p&gt;

&lt;p&gt;Every article on this website has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;number&lt;/code&gt; in its Front Matter. I want to use these sequence numbers to redirect to the main post. Using numbers as pretty URLs will make it easier for users to type it into the address bar.&lt;/p&gt;

&lt;h2 id=&quot;the-plan&quot;&gt;The plan&lt;/h2&gt;
&lt;p&gt;My article on &lt;a href=&quot;/2021/07/getting-started-with-argocd&quot;&gt;Getting started with ArgoCD&lt;/a&gt; is &lt;a href=&quot;https://gitlab.com/ayush-sharma/ayushsharma-in/-/blob/2.0/_posts/2021-07-06-getting-started-with-argocd.md#L4&quot;&gt;article 94&lt;/a&gt;. So I want &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://&amp;lt;my-pretty-domain&amp;gt;/94&lt;/code&gt; to redirect to the full article. Any other link for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://&amp;lt;my-pretty-domain&amp;gt;&lt;/code&gt; which doesn’t contain a valid article number should redirect to the primary domain, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ayushsharma.in&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;By breaking down the plan into individual tasks, I get the following list:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Register pretty domain and point it to Netlify.&lt;/li&gt;
  &lt;li&gt;Redirect pretty URLs for articles to full URL.&lt;/li&gt;
  &lt;li&gt;Redirect all other pretty URLs to primary domain, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ayushsharma.in&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;register-pretty-domain-and-point-it-to-netlify&quot;&gt;Register pretty domain and point it to Netlify&lt;/h2&gt;
&lt;p&gt;The first tasks is to get a pretty domain.&lt;/p&gt;

&lt;p&gt;I’ve registered &lt;a href=&quot;https://ayush.to&quot;&gt;ayush.to&lt;/a&gt; as my  pretty URL and pointed it to Netlify. There are several options on how to &lt;a href=&quot;https://docs.netlify.com/domains-https/custom-domains/configure-external-dns/#configure-an-apex-domain&quot;&gt;point a domain to Netlify’s load balancers&lt;/a&gt;. I’m pointing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ayush.to&lt;/code&gt;’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A record&lt;/code&gt; to Netlify which you can verify with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nslookup&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; nslookup ayush.to

Server:		10.1.0.1
Address:	10.1.0.1#53

Non-authoritative answer:
Name:	ayush.to
Address: 75.2.60.5
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Additionally, I’ve also added &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ayush.to&lt;/code&gt; as a &lt;a href=&quot;https://docs.netlify.com/domains-https/custom-domains/multiple-domains/#domain-aliases&quot;&gt;domain alias&lt;/a&gt; so Netlify can issue the SSL certs.&lt;/p&gt;

&lt;h2 id=&quot;redirect-pretty-urls-for-articles-to-correct-url&quot;&gt;Redirect pretty URLs for articles to correct URL.&lt;/h2&gt;
&lt;p&gt;Netlify offers two ways of handling redirections for static websites: &lt;a href=&quot;https://docs.netlify.com/routing/redirects/#syntax-for-the-redirects-file&quot;&gt;_redirects file&lt;/a&gt; and &lt;a href=&quot;https://docs.netlify.com/routing/redirects/#syntax-for-the-netlify-configuration-file&quot;&gt;Netlify.toml&lt;/a&gt;. Both options use a different syntax for the redirection and provide different features.&lt;/p&gt;

&lt;p&gt;Both options also have a specific &lt;a href=&quot;https://docs.netlify.com/routing/redirects/#rule-processing-order&quot;&gt;order of processing&lt;/a&gt;. Netlify processes the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_redirects&lt;/code&gt; file before &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Netlify.toml&lt;/code&gt;. So my plan is to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_redirects&lt;/code&gt; for the actual redirects and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Netlify.toml&lt;/code&gt; for the catch-all.&lt;/p&gt;

&lt;p&gt;To create the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_redirects&lt;/code&gt; file I’ll use the following code:&lt;/p&gt;

&lt;div class=&quot;language-liquid highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;---
---
&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;posts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;posts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'number'&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;posts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}{{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;prepend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://ayush.to/&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;prepend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;baseurl&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;prepend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;endfor&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Jekyll &lt;a href=&quot;https://jekyllrb.com/docs/structure/&quot;&gt;ignores files beginning with an underscore (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_&lt;/code&gt;)&lt;/a&gt; by default. So we’ll need to include it in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_config.yml&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;_redirects&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After the build process completes, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_redirects&lt;/code&gt; file in the destination folder looks like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;https://ayush.to/0    https://www.ayushsharma.in/2016/08/hello-world
https://ayush.to/1    https://www.ayushsharma.in/2016/08/douglas-noel-adams
https://ayush.to/2    https://www.ayushsharma.in/2021/08/introduction-to-ansible
https://ayush.to/3    https://www.ayushsharma.in/2016/08/json-web-tokens
https://ayush.to/4    https://www.ayushsharma.in/2016/08/nano-keyboard-shortcuts
https://ayush.to/5    https://www.ayushsharma.in/2016/08/introduction-to-fluentd
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If there were no syntax errors in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_redirects&lt;/code&gt; file, Netlify should confirm this by telling you the number of redirect rules it processed.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;https://www.ayush.nz/static/images/img-normal/2021-11-16-easy-pretty-url-redirects-with-jekyll-and-netlify-log.jpg&quot; width=&quot;1024&quot; alt=&quot;Netlify logs showing the total number of redirect rules processed.&quot; /&gt;
    &lt;figcaption&gt;&lt;a href=&quot;https://www.ayush.nz/static/images/img-normal/2021-11-16-easy-pretty-url-redirects-with-jekyll-and-netlify-log.jpg&quot; target=&quot;_blank&quot;&gt;&lt;i class=&quot;fas fa-search-plus me-1&quot;&gt;&lt;/i&gt;Enlarge&lt;/a&gt; — Netlify logs showing the total number of redirect rules processed.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I’m almost there! I’ve generated the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_redirects&lt;/code&gt; file. Now to handle the catch-all.&lt;/p&gt;

&lt;h2 id=&quot;redirect-all-other-pretty-urls-to-primary-domain&quot;&gt;Redirect all other pretty URLs to primary domain&lt;/h2&gt;
&lt;p&gt;My &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_redirects&lt;/code&gt; file can handle specific redirections. But I also need to set a catch-all.&lt;/p&gt;

&lt;p&gt;This is where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Netlify.toml&lt;/code&gt; comes in. In &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Netlify.toml&lt;/code&gt;, I’m going to tell Netlify to redirect everything to my primary domain. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Netlify.toml&lt;/code&gt; comes after &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_redirects&lt;/code&gt; in the order of processing. Meaning my catch-all only kicks in if no specific redirection rules matched. Nice!&lt;/p&gt;

&lt;p&gt;My &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Netlify.toml&lt;/code&gt; now contains the following code:&lt;/p&gt;

&lt;div class=&quot;language-toml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;## ayush.to&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;[[redirects]]&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://ayush.to/*&quot;&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://www.ayushsharma.in&quot;&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;301&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;force&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;nn&quot;&gt;[redirects.headers]&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;X-From&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Netlify&quot;&lt;/span&gt;

&lt;span class=&quot;nn&quot;&gt;[[redirects]]&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://www.ayush.to/*&quot;&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://www.ayushsharma.in&quot;&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;301&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;force&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;nn&quot;&gt;[redirects.headers]&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;X-From&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Netlify&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-test-drive&quot;&gt;The test drive!&lt;/h2&gt;
&lt;p&gt;Now let’s see if this whole thing even works.&lt;/p&gt;

&lt;p&gt;I’ve pushed the above code to Netlify and the robots have completed their task.&lt;/p&gt;

&lt;p&gt;So. If I go to &lt;a href=&quot;https://ayush.to/94&quot;&gt;ayush.to/94&lt;/a&gt; I should see the article on “Getting started with ArgoCD”.&lt;/p&gt;

&lt;p&gt;And…&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; curl https://ayush.to/94

Redirecting to https://www.ayushsharma.in/2021/07/getting-started-with-argocd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;… it works!&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Pretty URLs are a powerful feature in a tiny package. And I was able to set it up neatly and quickly thanks to Jekyll and Netlify.&lt;/p&gt;

&lt;p&gt;The ability to configure Netlify’s robots using simple scripts is why I love the service so much. There are of course plenty of improvements we can make. We could improve handling of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;www&lt;/code&gt; and non-&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;www&lt;/code&gt; versions of domains. Instead of using numbers as in this example, we could also use smaller slugs or post tags and categories. We can set one-off redirects for special occassions like sales or book launches. The list goes on and on.&lt;/p&gt;

&lt;p&gt;I hope the wheels in your head have begun turning. Let me know if you come up with a crazy new use for the Netlify redirection robots. Good luck, and happy coding :)&lt;/p&gt;

            </description>
        </item>
        
        <item>
            <title>Linking Jekyll pages back to their Git source code</title>
            <pubDate>Nov 16, 2021</pubDate>
            <link>https://www.ayush.nz/2021/11/linking-jekyll-pages-back-to-their-git-source-code</link>
            <guid isPermaLink="true">https://www.ayush.nz/2021/11/linking-jekyll-pages-back-to-their-git-source-code</guid>
            
            
            <description>
                &lt;p&gt;I’m a huge fan of open-source! One of the little ways I’ve supported the cause is by keeping this blog open since the very beginning. You can always &lt;a href=&quot;https://gitlab.com/ayush-sharma/ayushsharma-in&quot;&gt;browse the full source-code here&lt;/a&gt;. The reason is partly to let people see the history of changes behind each page. But also because I didn’t find many open-source Jekyll blogs to learn from when I started down this road. So I keep this website open in the hopes that my trials and errors will save someone else a lot of time.&lt;/p&gt;

&lt;p&gt;I was recently asked by someone (shout-out to Seth!) to send them the Markdown source of one of my articles. This gave me the idea to add a permanent “View source” button to the footer of every article page. I’ll walk you through the process of adding this to your own Jekyll website.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://jekyllrb.com/docs/variables/#page-variables&quot;&gt;Jekyll’s helpful variables&lt;/a&gt; have exactly what I was looking for: the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;page.path&lt;/code&gt;. This variable contains the raw filesystem path of each page. The official description even highlights its usefulness for linking back to the source!&lt;/p&gt;

&lt;p&gt;I’ll use my article on creating &lt;a href=&quot;/2021/10/make-linux-apps-for-notion-mastodon-webapps-using-nativefier&quot;&gt;Linux apps for webapps&lt;/a&gt; as an example. Printing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{{ page.path }}&lt;/code&gt; within the article’s Markdown file outputs the following:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;_posts/2021-10-10-make-linux-apps-for-notion-mastodon-webapps-using-nativefier.md
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The above article’s &lt;a href=&quot;https://gitlab.com/ayush-sharma/ayushsharma-in/-/blob/master/_posts/2021-10-10-make-linux-apps-for-notion-mastodon-webapps-using-nativefier.md&quot;&gt;source code&lt;/a&gt; is at the following path:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;https://gitlab.com/ayush-sharma/ayushsharma-in/-/blob/master/_posts/2021-10-10-make-linux-apps-for-notion-mastodon-webapps-using-nativefier.md
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This means that prepending &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://gitlab.com/ayush-sharma/ayushsharma-in/-/blob/master/&lt;/code&gt; to any article’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;page.path&lt;/code&gt; will generate a link back to its source code!&lt;/p&gt;

&lt;p&gt;In Jekyll, generating this full link will look like this:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{{ page.path | prepend: site.content.blog_source_prefix }}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;target=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_blank&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;View source&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And it’s that’s easy :)&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If you scroll down to the footer of this page, you should see a new “View source” link. Go ahead and try it out!&lt;/p&gt;

            </description>
        </item>
        
        <item>
            <title>Optimising JPG and PNG images for a Jekyll website</title>
            <pubDate>Nov 6, 2021</pubDate>
            <link>https://www.ayush.nz/2021/11/optimising-jpg-and-png-images-for-a-jekyll-blog</link>
            <guid isPermaLink="true">https://www.ayush.nz/2021/11/optimising-jpg-and-png-images-for-a-jekyll-blog</guid>
            
            
            <description>
                &lt;p&gt;This website has been text-oriented for a long time now. The decision to add article banners and thumbnails is a recent one. I stayed away from images for a long time since handling and optimising images can be a PITA. I was glad to find that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;optipng&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jpegoptim&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;imagemagick&lt;/code&gt; already exist to solve this problem for me.&lt;/p&gt;

&lt;p&gt;My plan is as follows.&lt;/p&gt;

&lt;p&gt;I already have all article images in my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;static/images&lt;/code&gt; folder. From there, I want to generate two copies of all PNG and JPG images. The first would be a cropped thumbnail version measuring 422-by-316. The second would be  a larger banner version measuring 1024x768.&lt;/p&gt;

&lt;p&gt;Both copies, the thumbnail and the banner, will be in folders of their own. I’ll then &lt;a href=&quot;/2021/08/using-variables-in-jekyll-to-define-custom-content&quot;&gt;leverage Jekyll’s custom variables&lt;/a&gt; for the folder paths.&lt;/p&gt;

&lt;h2 id=&quot;installing-the-binaries&quot;&gt;Installing the binaries&lt;/h2&gt;
&lt;p&gt;On my Mac OS X, installing the binaries required a single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew&lt;/code&gt; command.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;brew &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;optipng jpegoptim imagemagick
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;creating-folders-for-thumbnails-and-banners&quot;&gt;Creating folders for thumbnails and banners&lt;/h2&gt;
&lt;p&gt;Next, I’ll create new folders under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;static/images&lt;/code&gt;. Thumbnails will go in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;img-thumbs&lt;/code&gt; and banners will go in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;img-normal&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;static/images
&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; img-thumbs img-normal
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With the folders created, I’ll first copy all &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GIF&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SVG&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JPG&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PNG&lt;/code&gt; files to both folders. I’ll use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GIFs&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SVGs&lt;/code&gt; as-is for thumbnails and banner images.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cp &lt;/span&gt;content/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.gif img-thumbs/&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cp &lt;/span&gt;content/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.gif img-normal/
&lt;span class=&quot;nb&quot;&gt;cp &lt;/span&gt;content/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.svg img-thumbs/&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cp &lt;/span&gt;content/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.svg img-normal/
&lt;span class=&quot;nb&quot;&gt;cp &lt;/span&gt;content/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.jpg img-thumbs/&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cp &lt;/span&gt;content/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.jpg img-normal/
&lt;span class=&quot;nb&quot;&gt;cp &lt;/span&gt;content/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.png img-thumbs/&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cp &lt;/span&gt;content/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.png img-normal/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;processing-thumbnails&quot;&gt;Processing thumbnails&lt;/h2&gt;
&lt;p&gt;First let’s resize and optimise the thumbnails. As mentioned earlier, I want the thumbnails to be 422-by-316. I’ll use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mogrify&lt;/code&gt; command from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImageMagick&lt;/code&gt; to resize the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JPGs&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PNGs&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;img-thumbs
mogrify &lt;span class=&quot;nt&quot;&gt;-resize&lt;/span&gt; 422x316 &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.png
mogrify &lt;span class=&quot;nt&quot;&gt;-format&lt;/span&gt; jpg &lt;span class=&quot;nt&quot;&gt;-resize&lt;/span&gt; 422x316 &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.jpg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now let’s optimise the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PNGs&lt;/code&gt; using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;optipng&lt;/code&gt; and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JPGs&lt;/code&gt; using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jpegoptim&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;i &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.png&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do &lt;/span&gt;optipng &lt;span class=&quot;nt&quot;&gt;-o5&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-quiet&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$i&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;done
&lt;/span&gt;jpegoptim &lt;span class=&quot;nt&quot;&gt;-sq&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.jpg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the above command:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;For &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;optipng&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-o5&lt;/code&gt; swtich sets the level of optimisation, with zero being the lowest.&lt;/li&gt;
  &lt;li&gt;For &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jpegoptim&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-s&lt;/code&gt; strips all image metadata, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-q&lt;/code&gt; sets quiet mode.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;processing-banners&quot;&gt;Processing banners&lt;/h2&gt;
&lt;p&gt;I’ll process the banner images like I processed the thumbnails above. Other than the file dimensions everything else will stay the same.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ..
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;img-normal
mogrify &lt;span class=&quot;nt&quot;&gt;-resize&lt;/span&gt; 1024x768 &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.png
mogrify &lt;span class=&quot;nt&quot;&gt;-format&lt;/span&gt; jpg &lt;span class=&quot;nt&quot;&gt;-resize&lt;/span&gt; 1024x768 &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.jpg
&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;i &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.png&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do &lt;/span&gt;optipng &lt;span class=&quot;nt&quot;&gt;-o5&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-quiet&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$i&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;done
&lt;/span&gt;jpegoptim &lt;span class=&quot;nt&quot;&gt;-sq&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.jpg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;configuring-the-paths-in-jekyll&quot;&gt;Configuring the paths in Jekyll&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;img-thumbs&lt;/code&gt; now contains my thumbnails and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;img-normal&lt;/code&gt; contains the banners. To make my life easier, I’ll set both of them to &lt;a href=&quot;/2021/08/using-variables-in-jekyll-to-define-custom-content&quot;&gt;Jekyll variables in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_config.yml&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;content-images-path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/static/images/img-normal/&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;content-thumbs-images-path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/static/images/img-thumbs/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Using these is simple. When I want to display the thumbnail I’ll prepend &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content-thumbs-images-path&lt;/code&gt; to the image. When I want to display the full banner I’ll prepend &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content-images-path&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, articles on this site now have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;banner_img&lt;/code&gt; property in the front matter. The value of this property is the name of the image file. To display the thumbnail for the article, I use the following code:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;% &lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;page.banner_img %&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
 &amp;lt;img &lt;span class=&quot;nv&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;{{ page.banner_img | prepend: site.content-images-path | prepend: site.baseurl | prepend: site.url }}&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Banner image for {{ page.title }}&quot;&lt;/span&gt; /&amp;gt;
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;% endif %&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;There are several improvements we can make to the commands above. The most obvious might be to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rsync&lt;/code&gt; to only copy changed files to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;img-thumbs&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;img-normal&lt;/code&gt;. That way we’re not re-processing files over and over again. Another improvement might be to add those commands to Git pre-commit hooks or a CI pipeline.&lt;/p&gt;

&lt;p&gt;Resizing and optimising images to reduce their size is a win for the user and the web as a whole. Fewer bytes transmitted over the wire means a lower carbon footprint, but that’s another article. The UX victory is good enough for now :)&lt;/p&gt;

&lt;p&gt;Happy coding :)&lt;/p&gt;

            </description>
        </item>
        
        <item>
            <title>Quick and easy client-side JavaScript search with Lunr.js</title>
            <pubDate>Nov 1, 2021</pubDate>
            <link>https://www.ayush.nz/2021/11/using-lunr-js-to-create-client-side-website-search-index</link>
            <guid isPermaLink="true">https://www.ayush.nz/2021/11/using-lunr-js-to-create-client-side-website-search-index</guid>
            
            
            <description>
                &lt;p&gt;Search is a must-have for any website or application. A simple search widget can allow users to comb through your entire blog. Or allow customers to browse your inventory. Building a custom photo gallery? Add a search box. Website search functionality is available from a variety of third-party vendors. Or you can take the DIY approach and build the entire backend to answer search API calls.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://lunrjs.com/&quot;&gt;Lunr.js&lt;/a&gt; works on the client-side via JavaScript. Instead of sending calls to a backend, Lunr looks up search terms in an index built on the client-side itself. This avoids expensive back-and-forth network calls between the browser and your server. There are plenty of tutorials online to showcase Lunr’s website search functionality. But you can actually use Lunr.js to search any array of JavaScript objects.&lt;/p&gt;

&lt;p&gt;In this how-to, I’ll build a search index for the &lt;a href=&quot;https://raw.githubusercontent.com/benoitvallon/100-best-books/master/books.json&quot;&gt;top 100 books of all time&lt;/a&gt;. After that, I’ll show you how to pre-build the index for faster indexing. I’ll also show you how to make the most of Lunr’s search options. And finally, I’ll show off &lt;a href=&quot;https://findmymastodon.com&quot;&gt;findmymastodon.com&lt;/a&gt; - a real-world implementation of Lunr.&lt;/p&gt;

&lt;h2 id=&quot;getting-started-with-lunrjs&quot;&gt;Getting started with Lunr.js&lt;/h2&gt;

&lt;p&gt;Create a new HTML page called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lunr.html&lt;/code&gt;. We’ll use this file throughout this guide. At the top of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lunr.html&lt;/code&gt;, call the main Lunr JS library.&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://unpkg.com/lunr/lunr.js&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note: You can find the &lt;a href=&quot;https://gitlab.com/ayush-sharma/example-assets/-/blob/main/front-end/js/lunr-tutorial.html&quot;&gt;complete code here&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;loading-the-dataset&quot;&gt;Loading the dataset&lt;/h3&gt;

&lt;p&gt;Next, create a variable called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;my_big_json&lt;/code&gt;. This variable will contain the JSON-ified string of our main dataset. Define the variable in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lunr.html&lt;/code&gt; within &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;my_big_json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;[{&quot;author&quot;:&quot;Chinua Achebe&quot;,&quot;country&quot;:&quot;Nigeria&quot;,&quot;imageLink&quot;:&quot;images/things-fall-apart.jpg&quot;,&quot;language&quot;:&quot;English&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Things_Fall_Apart&quot;,&quot;pages&quot;:209,&quot;title&quot;:&quot;Things Fall Apart&quot;,&quot;year&quot;:1958},{&quot;author&quot;:&quot;Hans Christian Andersen&quot;,&quot;country&quot;:&quot;Denmark&quot;,&quot;imageLink&quot;:&quot;images/fairy-tales.jpg&quot;,&quot;language&quot;:&quot;Danish&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Fairy_Tales_Told_for_Children._First_Collection.&quot;,&quot;pages&quot;:784,&quot;title&quot;:&quot;Fairy tales&quot;,&quot;year&quot;:1836},{&quot;author&quot;:&quot;Dante Alighieri&quot;,&quot;country&quot;:&quot;Italy&quot;,&quot;imageLink&quot;:&quot;images/the-divine-comedy.jpg&quot;,&quot;language&quot;:&quot;Italian&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Divine_Comedy&quot;,&quot;pages&quot;:928,&quot;title&quot;:&quot;The Divine Comedy&quot;,&quot;year&quot;:1315},{&quot;author&quot;:&quot;Unknown&quot;,&quot;country&quot;:&quot;Sumer and Akkadian Empire&quot;,&quot;imageLink&quot;:&quot;images/the-epic-of-gilgamesh.jpg&quot;,&quot;language&quot;:&quot;Akkadian&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Epic_of_Gilgamesh&quot;,&quot;pages&quot;:160,&quot;title&quot;:&quot;The Epic Of Gilgamesh&quot;,&quot;year&quot;:-1700},{&quot;author&quot;:&quot;Unknown&quot;,&quot;country&quot;:&quot;Achaemenid Empire&quot;,&quot;imageLink&quot;:&quot;images/the-book-of-job.jpg&quot;,&quot;language&quot;:&quot;Hebrew&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Book_of_Job&quot;,&quot;pages&quot;:176,&quot;title&quot;:&quot;The Book Of Job&quot;,&quot;year&quot;:-600},{&quot;author&quot;:&quot;Unknown&quot;,&quot;country&quot;:&quot;India/Iran/Iraq/Egypt/Tajikistan&quot;,&quot;imageLink&quot;:&quot;images/one-thousand-and-one-nights.jpg&quot;,&quot;language&quot;:&quot;Arabic&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/One_Thousand_and_One_Nights&quot;,&quot;pages&quot;:288,&quot;title&quot;:&quot;One Thousand and One Nights&quot;,&quot;year&quot;:1200},{&quot;author&quot;:&quot;Unknown&quot;,&quot;country&quot;:&quot;Iceland&quot;,&quot;imageLink&quot;:&quot;images/njals-saga.jpg&quot;,&quot;language&quot;:&quot;Old Norse&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Nj%C3%A1ls_saga&quot;,&quot;pages&quot;:384,&quot;title&quot;:&quot;Njál&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;u0027s Saga&quot;,&quot;year&quot;:1350},{&quot;author&quot;:&quot;Jane Austen&quot;,&quot;country&quot;:&quot;United Kingdom&quot;,&quot;imageLink&quot;:&quot;images/pride-and-prejudice.jpg&quot;,&quot;language&quot;:&quot;English&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Pride_and_Prejudice&quot;,&quot;pages&quot;:226,&quot;title&quot;:&quot;Pride and Prejudice&quot;,&quot;year&quot;:1813},{&quot;author&quot;:&quot;Honoré de Balzac&quot;,&quot;country&quot;:&quot;France&quot;,&quot;imageLink&quot;:&quot;images/le-pere-goriot.jpg&quot;,&quot;language&quot;:&quot;French&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Le_P%C3%A8re_Goriot&quot;,&quot;pages&quot;:443,&quot;title&quot;:&quot;Le Père Goriot&quot;,&quot;year&quot;:1835},{&quot;author&quot;:&quot;Samuel Beckett&quot;,&quot;country&quot;:&quot;Republic of Ireland&quot;,&quot;imageLink&quot;:&quot;images/molloy-malone-dies-the-unnamable.jpg&quot;,&quot;language&quot;:&quot;French, English&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Molloy_(novel)&quot;,&quot;pages&quot;:256,&quot;title&quot;:&quot;Molloy, Malone Dies, The Unnamable, the trilogy&quot;,&quot;year&quot;:1952},{&quot;author&quot;:&quot;Giovanni Boccaccio&quot;,&quot;country&quot;:&quot;Italy&quot;,&quot;imageLink&quot;:&quot;images/the-decameron.jpg&quot;,&quot;language&quot;:&quot;Italian&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/The_Decameron&quot;,&quot;pages&quot;:1024,&quot;title&quot;:&quot;The Decameron&quot;,&quot;year&quot;:1351},{&quot;author&quot;:&quot;Jorge Luis Borges&quot;,&quot;country&quot;:&quot;Argentina&quot;,&quot;imageLink&quot;:&quot;images/ficciones.jpg&quot;,&quot;language&quot;:&quot;Spanish&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Ficciones&quot;,&quot;pages&quot;:224,&quot;title&quot;:&quot;Ficciones&quot;,&quot;year&quot;:1965},{&quot;author&quot;:&quot;Emily Brontë&quot;,&quot;country&quot;:&quot;United Kingdom&quot;,&quot;imageLink&quot;:&quot;images/wuthering-heights.jpg&quot;,&quot;language&quot;:&quot;English&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Wuthering_Heights&quot;,&quot;pages&quot;:342,&quot;title&quot;:&quot;Wuthering Heights&quot;,&quot;year&quot;:1847},{&quot;author&quot;:&quot;Albert Camus&quot;,&quot;country&quot;:&quot;Algeria, French Empire&quot;,&quot;imageLink&quot;:&quot;images/l-etranger.jpg&quot;,&quot;language&quot;:&quot;French&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/The_Stranger_(novel)&quot;,&quot;pages&quot;:185,&quot;title&quot;:&quot;The Stranger&quot;,&quot;year&quot;:1942},{&quot;author&quot;:&quot;Paul Celan&quot;,&quot;country&quot;:&quot;Romania, France&quot;,&quot;imageLink&quot;:&quot;images/poems-paul-celan.jpg&quot;,&quot;language&quot;:&quot;German&quot;,&quot;link&quot;:&quot;&quot;,&quot;pages&quot;:320,&quot;title&quot;:&quot;Poems&quot;,&quot;year&quot;:1952},{&quot;author&quot;:&quot;Louis-Ferdinand Céline&quot;,&quot;country&quot;:&quot;France&quot;,&quot;imageLink&quot;:&quot;images/voyage-au-bout-de-la-nuit.jpg&quot;,&quot;language&quot;:&quot;French&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Journey_to_the_End_of_the_Night&quot;,&quot;pages&quot;:505,&quot;title&quot;:&quot;Journey to the End of the Night&quot;,&quot;year&quot;:1932},{&quot;author&quot;:&quot;Miguel de Cervantes&quot;,&quot;country&quot;:&quot;Spain&quot;,&quot;imageLink&quot;:&quot;images/don-quijote-de-la-mancha.jpg&quot;,&quot;language&quot;:&quot;Spanish&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Don_Quixote&quot;,&quot;pages&quot;:1056,&quot;title&quot;:&quot;Don Quijote De La Mancha&quot;,&quot;year&quot;:1610},{&quot;author&quot;:&quot;Geoffrey Chaucer&quot;,&quot;country&quot;:&quot;England&quot;,&quot;imageLink&quot;:&quot;images/the-canterbury-tales.jpg&quot;,&quot;language&quot;:&quot;English&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/The_Canterbury_Tales&quot;,&quot;pages&quot;:544,&quot;title&quot;:&quot;The Canterbury Tales&quot;,&quot;year&quot;:1450},{&quot;author&quot;:&quot;Anton Chekhov&quot;,&quot;country&quot;:&quot;Russia&quot;,&quot;imageLink&quot;:&quot;images/stories-of-anton-chekhov.jpg&quot;,&quot;language&quot;:&quot;Russian&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/List_of_short_stories_by_Anton_Chekhov&quot;,&quot;pages&quot;:194,&quot;title&quot;:&quot;Stories&quot;,&quot;year&quot;:1886},{&quot;author&quot;:&quot;Joseph Conrad&quot;,&quot;country&quot;:&quot;United Kingdom&quot;,&quot;imageLink&quot;:&quot;images/nostromo.jpg&quot;,&quot;language&quot;:&quot;English&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Nostromo&quot;,&quot;pages&quot;:320,&quot;title&quot;:&quot;Nostromo&quot;,&quot;year&quot;:1904},{&quot;author&quot;:&quot;Charles Dickens&quot;,&quot;country&quot;:&quot;United Kingdom&quot;,&quot;imageLink&quot;:&quot;images/great-expectations.jpg&quot;,&quot;language&quot;:&quot;English&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Great_Expectations&quot;,&quot;pages&quot;:194,&quot;title&quot;:&quot;Great Expectations&quot;,&quot;year&quot;:1861},{&quot;author&quot;:&quot;Denis Diderot&quot;,&quot;country&quot;:&quot;France&quot;,&quot;imageLink&quot;:&quot;images/jacques-the-fatalist.jpg&quot;,&quot;language&quot;:&quot;French&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Jacques_the_Fatalist&quot;,&quot;pages&quot;:596,&quot;title&quot;:&quot;Jacques the Fatalist&quot;,&quot;year&quot;:1796},{&quot;author&quot;:&quot;Alfred Döblin&quot;,&quot;country&quot;:&quot;Germany&quot;,&quot;imageLink&quot;:&quot;images/berlin-alexanderplatz.jpg&quot;,&quot;language&quot;:&quot;German&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Berlin_Alexanderplatz&quot;,&quot;pages&quot;:600,&quot;title&quot;:&quot;Berlin Alexanderplatz&quot;,&quot;year&quot;:1929},{&quot;author&quot;:&quot;Fyodor Dostoevsky&quot;,&quot;country&quot;:&quot;Russia&quot;,&quot;imageLink&quot;:&quot;images/crime-and-punishment.jpg&quot;,&quot;language&quot;:&quot;Russian&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Crime_and_Punishment&quot;,&quot;pages&quot;:551,&quot;title&quot;:&quot;Crime and Punishment&quot;,&quot;year&quot;:1866},{&quot;author&quot;:&quot;Fyodor Dostoevsky&quot;,&quot;country&quot;:&quot;Russia&quot;,&quot;imageLink&quot;:&quot;images/the-idiot.jpg&quot;,&quot;language&quot;:&quot;Russian&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/The_Idiot&quot;,&quot;pages&quot;:656,&quot;title&quot;:&quot;The Idiot&quot;,&quot;year&quot;:1869},{&quot;author&quot;:&quot;Fyodor Dostoevsky&quot;,&quot;country&quot;:&quot;Russia&quot;,&quot;imageLink&quot;:&quot;images/the-possessed.jpg&quot;,&quot;language&quot;:&quot;Russian&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Demons_(Dostoyevsky_novel)&quot;,&quot;pages&quot;:768,&quot;title&quot;:&quot;The Possessed&quot;,&quot;year&quot;:1872},{&quot;author&quot;:&quot;Fyodor Dostoevsky&quot;,&quot;country&quot;:&quot;Russia&quot;,&quot;imageLink&quot;:&quot;images/the-brothers-karamazov.jpg&quot;,&quot;language&quot;:&quot;Russian&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/The_Brothers_Karamazov&quot;,&quot;pages&quot;:824,&quot;title&quot;:&quot;The Brothers Karamazov&quot;,&quot;year&quot;:1880},{&quot;author&quot;:&quot;George Eliot&quot;,&quot;country&quot;:&quot;United Kingdom&quot;,&quot;imageLink&quot;:&quot;images/middlemarch.jpg&quot;,&quot;language&quot;:&quot;English&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Middlemarch&quot;,&quot;pages&quot;:800,&quot;title&quot;:&quot;Middlemarch&quot;,&quot;year&quot;:1871},{&quot;author&quot;:&quot;Ralph Ellison&quot;,&quot;country&quot;:&quot;United States&quot;,&quot;imageLink&quot;:&quot;images/invisible-man.jpg&quot;,&quot;language&quot;:&quot;English&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Invisible_Man&quot;,&quot;pages&quot;:581,&quot;title&quot;:&quot;Invisible Man&quot;,&quot;year&quot;:1952},{&quot;author&quot;:&quot;Euripides&quot;,&quot;country&quot;:&quot;Greece&quot;,&quot;imageLink&quot;:&quot;images/medea.jpg&quot;,&quot;language&quot;:&quot;Greek&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Medea_(play)&quot;,&quot;pages&quot;:104,&quot;title&quot;:&quot;Medea&quot;,&quot;year&quot;:-431},{&quot;author&quot;:&quot;William Faulkner&quot;,&quot;country&quot;:&quot;United States&quot;,&quot;imageLink&quot;:&quot;images/absalom-absalom.jpg&quot;,&quot;language&quot;:&quot;English&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Absalom,_Absalom!&quot;,&quot;pages&quot;:313,&quot;title&quot;:&quot;Absalom, Absalom!&quot;,&quot;year&quot;:1936},{&quot;author&quot;:&quot;William Faulkner&quot;,&quot;country&quot;:&quot;United States&quot;,&quot;imageLink&quot;:&quot;images/the-sound-and-the-fury.jpg&quot;,&quot;language&quot;:&quot;English&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/The_Sound_and_the_Fury&quot;,&quot;pages&quot;:326,&quot;title&quot;:&quot;The Sound and the Fury&quot;,&quot;year&quot;:1929},{&quot;author&quot;:&quot;Gustave Flaubert&quot;,&quot;country&quot;:&quot;France&quot;,&quot;imageLink&quot;:&quot;images/madame-bovary.jpg&quot;,&quot;language&quot;:&quot;French&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Madame_Bovary&quot;,&quot;pages&quot;:528,&quot;title&quot;:&quot;Madame Bovary&quot;,&quot;year&quot;:1857},{&quot;author&quot;:&quot;Gustave Flaubert&quot;,&quot;country&quot;:&quot;France&quot;,&quot;imageLink&quot;:&quot;images/l-education-sentimentale.jpg&quot;,&quot;language&quot;:&quot;French&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Sentimental_Education&quot;,&quot;pages&quot;:606,&quot;title&quot;:&quot;Sentimental Education&quot;,&quot;year&quot;:1869},{&quot;author&quot;:&quot;Federico García Lorca&quot;,&quot;country&quot;:&quot;Spain&quot;,&quot;imageLink&quot;:&quot;images/gypsy-ballads.jpg&quot;,&quot;language&quot;:&quot;Spanish&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Gypsy_Ballads&quot;,&quot;pages&quot;:218,&quot;title&quot;:&quot;Gypsy Ballads&quot;,&quot;year&quot;:1928},{&quot;author&quot;:&quot;Gabriel García Márquez&quot;,&quot;country&quot;:&quot;Colombia&quot;,&quot;imageLink&quot;:&quot;images/one-hundred-years-of-solitude.jpg&quot;,&quot;language&quot;:&quot;Spanish&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/One_Hundred_Years_of_Solitude&quot;,&quot;pages&quot;:417,&quot;title&quot;:&quot;One Hundred Years of Solitude&quot;,&quot;year&quot;:1967},{&quot;author&quot;:&quot;Gabriel García Márquez&quot;,&quot;country&quot;:&quot;Colombia&quot;,&quot;imageLink&quot;:&quot;images/love-in-the-time-of-cholera.jpg&quot;,&quot;language&quot;:&quot;Spanish&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Love_in_the_Time_of_Cholera&quot;,&quot;pages&quot;:368,&quot;title&quot;:&quot;Love in the Time of Cholera&quot;,&quot;year&quot;:1985},{&quot;author&quot;:&quot;Johann Wolfgang von Goethe&quot;,&quot;country&quot;:&quot;Saxe-Weimar&quot;,&quot;imageLink&quot;:&quot;images/faust.jpg&quot;,&quot;language&quot;:&quot;German&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Goethe%27s_Faust&quot;,&quot;pages&quot;:158,&quot;title&quot;:&quot;Faust&quot;,&quot;year&quot;:1832},{&quot;author&quot;:&quot;Nikolai Gogol&quot;,&quot;country&quot;:&quot;Russia&quot;,&quot;imageLink&quot;:&quot;images/dead-souls.jpg&quot;,&quot;language&quot;:&quot;Russian&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Dead_Souls&quot;,&quot;pages&quot;:432,&quot;title&quot;:&quot;Dead Souls&quot;,&quot;year&quot;:1842},{&quot;author&quot;:&quot;Günter Grass&quot;,&quot;country&quot;:&quot;Germany&quot;,&quot;imageLink&quot;:&quot;images/the-tin-drum.jpg&quot;,&quot;language&quot;:&quot;German&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/The_Tin_Drum&quot;,&quot;pages&quot;:600,&quot;title&quot;:&quot;The Tin Drum&quot;,&quot;year&quot;:1959},{&quot;author&quot;:&quot;João Guimarães Rosa&quot;,&quot;country&quot;:&quot;Brazil&quot;,&quot;imageLink&quot;:&quot;images/the-devil-to-pay-in-the-backlands.jpg&quot;,&quot;language&quot;:&quot;Portuguese&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/The_Devil_to_Pay_in_the_Backlands&quot;,&quot;pages&quot;:494,&quot;title&quot;:&quot;The Devil to Pay in the Backlands&quot;,&quot;year&quot;:1956},{&quot;author&quot;:&quot;Knut Hamsun&quot;,&quot;country&quot;:&quot;Norway&quot;,&quot;imageLink&quot;:&quot;images/hunger.jpg&quot;,&quot;language&quot;:&quot;Norwegian&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Hunger_(Hamsun_novel)&quot;,&quot;pages&quot;:176,&quot;title&quot;:&quot;Hunger&quot;,&quot;year&quot;:1890},{&quot;author&quot;:&quot;Ernest Hemingway&quot;,&quot;country&quot;:&quot;United States&quot;,&quot;imageLink&quot;:&quot;images/the-old-man-and-the-sea.jpg&quot;,&quot;language&quot;:&quot;English&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/The_Old_Man_and_the_Sea&quot;,&quot;pages&quot;:128,&quot;title&quot;:&quot;The Old Man and the Sea&quot;,&quot;year&quot;:1952},{&quot;author&quot;:&quot;Homer&quot;,&quot;country&quot;:&quot;Greece&quot;,&quot;imageLink&quot;:&quot;images/the-iliad-of-homer.jpg&quot;,&quot;language&quot;:&quot;Greek&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Iliad&quot;,&quot;pages&quot;:608,&quot;title&quot;:&quot;Iliad&quot;,&quot;year&quot;:-735},{&quot;author&quot;:&quot;Homer&quot;,&quot;country&quot;:&quot;Greece&quot;,&quot;imageLink&quot;:&quot;images/the-odyssey-of-homer.jpg&quot;,&quot;language&quot;:&quot;Greek&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Odyssey&quot;,&quot;pages&quot;:374,&quot;title&quot;:&quot;Odyssey&quot;,&quot;year&quot;:-800},{&quot;author&quot;:&quot;Henrik Ibsen&quot;,&quot;country&quot;:&quot;Norway&quot;,&quot;imageLink&quot;:&quot;images/a-Dolls-house.jpg&quot;,&quot;language&quot;:&quot;Norwegian&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/A_Doll%27s_House&quot;,&quot;pages&quot;:68,&quot;title&quot;:&quot;A Doll&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;u0027s House&quot;,&quot;year&quot;:1879},{&quot;author&quot;:&quot;James Joyce&quot;,&quot;country&quot;:&quot;Irish Free State&quot;,&quot;imageLink&quot;:&quot;images/ulysses.jpg&quot;,&quot;language&quot;:&quot;English&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Ulysses_(novel)&quot;,&quot;pages&quot;:228,&quot;title&quot;:&quot;Ulysses&quot;,&quot;year&quot;:1922},{&quot;author&quot;:&quot;Franz Kafka&quot;,&quot;country&quot;:&quot;Czechoslovakia&quot;,&quot;imageLink&quot;:&quot;images/stories-of-franz-kafka.jpg&quot;,&quot;language&quot;:&quot;German&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Franz_Kafka_bibliography#Short_stories&quot;,&quot;pages&quot;:488,&quot;title&quot;:&quot;Stories&quot;,&quot;year&quot;:1924},{&quot;author&quot;:&quot;Franz Kafka&quot;,&quot;country&quot;:&quot;Czechoslovakia&quot;,&quot;imageLink&quot;:&quot;images/the-trial.jpg&quot;,&quot;language&quot;:&quot;German&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/The_Trial&quot;,&quot;pages&quot;:160,&quot;title&quot;:&quot;The Trial&quot;,&quot;year&quot;:1925},{&quot;author&quot;:&quot;Franz Kafka&quot;,&quot;country&quot;:&quot;Czechoslovakia&quot;,&quot;imageLink&quot;:&quot;images/the-castle.jpg&quot;,&quot;language&quot;:&quot;German&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/The_Castle_(novel)&quot;,&quot;pages&quot;:352,&quot;title&quot;:&quot;The Castle&quot;,&quot;year&quot;:1926},{&quot;author&quot;:&quot;Kālidāsa&quot;,&quot;country&quot;:&quot;India&quot;,&quot;imageLink&quot;:&quot;images/the-recognition-of-shakuntala.jpg&quot;,&quot;language&quot;:&quot;Sanskrit&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Abhij%C3%B1%C4%81na%C5%9B%C4%81kuntalam&quot;,&quot;pages&quot;:147,&quot;title&quot;:&quot;The recognition of Shakuntala&quot;,&quot;year&quot;:150},{&quot;author&quot;:&quot;Yasunari Kawabata&quot;,&quot;country&quot;:&quot;Japan&quot;,&quot;imageLink&quot;:&quot;images/the-sound-of-the-mountain.jpg&quot;,&quot;language&quot;:&quot;Japanese&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/The_Sound_of_the_Mountain&quot;,&quot;pages&quot;:288,&quot;title&quot;:&quot;The Sound of the Mountain&quot;,&quot;year&quot;:1954},{&quot;author&quot;:&quot;Nikos Kazantzakis&quot;,&quot;country&quot;:&quot;Greece&quot;,&quot;imageLink&quot;:&quot;images/zorba-the-greek.jpg&quot;,&quot;language&quot;:&quot;Greek&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Zorba_the_Greek&quot;,&quot;pages&quot;:368,&quot;title&quot;:&quot;Zorba the Greek&quot;,&quot;year&quot;:1946},{&quot;author&quot;:&quot;D. H. Lawrence&quot;,&quot;country&quot;:&quot;United Kingdom&quot;,&quot;imageLink&quot;:&quot;images/sons-and-lovers.jpg&quot;,&quot;language&quot;:&quot;English&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Sons_and_Lovers&quot;,&quot;pages&quot;:432,&quot;title&quot;:&quot;Sons and Lovers&quot;,&quot;year&quot;:1913},{&quot;author&quot;:&quot;Halldór Laxness&quot;,&quot;country&quot;:&quot;Iceland&quot;,&quot;imageLink&quot;:&quot;images/independent-people.jpg&quot;,&quot;language&quot;:&quot;Icelandic&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Independent_People&quot;,&quot;pages&quot;:470,&quot;title&quot;:&quot;Independent People&quot;,&quot;year&quot;:1934},{&quot;author&quot;:&quot;Giacomo Leopardi&quot;,&quot;country&quot;:&quot;Italy&quot;,&quot;imageLink&quot;:&quot;images/poems-giacomo-leopardi.jpg&quot;,&quot;language&quot;:&quot;Italian&quot;,&quot;link&quot;:&quot;&quot;,&quot;pages&quot;:184,&quot;title&quot;:&quot;Poems&quot;,&quot;year&quot;:1818},{&quot;author&quot;:&quot;Doris Lessing&quot;,&quot;country&quot;:&quot;United Kingdom&quot;,&quot;imageLink&quot;:&quot;images/the-golden-notebook.jpg&quot;,&quot;language&quot;:&quot;English&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/The_Golden_Notebook&quot;,&quot;pages&quot;:688,&quot;title&quot;:&quot;The Golden Notebook&quot;,&quot;year&quot;:1962},{&quot;author&quot;:&quot;Astrid Lindgren&quot;,&quot;country&quot;:&quot;Sweden&quot;,&quot;imageLink&quot;:&quot;images/pippi-longstocking.jpg&quot;,&quot;language&quot;:&quot;Swedish&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Pippi_Longstocking&quot;,&quot;pages&quot;:160,&quot;title&quot;:&quot;Pippi Longstocking&quot;,&quot;year&quot;:1945},{&quot;author&quot;:&quot;Lu Xun&quot;,&quot;country&quot;:&quot;China&quot;,&quot;imageLink&quot;:&quot;images/diary-of-a-madman.jpg&quot;,&quot;language&quot;:&quot;Chinese&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/A_Madman%27s_Diary&quot;,&quot;pages&quot;:389,&quot;title&quot;:&quot;Diary of a Madman&quot;,&quot;year&quot;:1918},{&quot;author&quot;:&quot;Naguib Mahfouz&quot;,&quot;country&quot;:&quot;Egypt&quot;,&quot;imageLink&quot;:&quot;images/children-of-gebelawi.jpg&quot;,&quot;language&quot;:&quot;Arabic&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Children_of_Gebelawi&quot;,&quot;pages&quot;:355,&quot;title&quot;:&quot;Children of Gebelawi&quot;,&quot;year&quot;:1959},{&quot;author&quot;:&quot;Thomas Mann&quot;,&quot;country&quot;:&quot;Germany&quot;,&quot;imageLink&quot;:&quot;images/buddenbrooks.jpg&quot;,&quot;language&quot;:&quot;German&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Buddenbrooks&quot;,&quot;pages&quot;:736,&quot;title&quot;:&quot;Buddenbrooks&quot;,&quot;year&quot;:1901},{&quot;author&quot;:&quot;Thomas Mann&quot;,&quot;country&quot;:&quot;Germany&quot;,&quot;imageLink&quot;:&quot;images/the-magic-mountain.jpg&quot;,&quot;language&quot;:&quot;German&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/The_Magic_Mountain&quot;,&quot;pages&quot;:720,&quot;title&quot;:&quot;The Magic Mountain&quot;,&quot;year&quot;:1924},{&quot;author&quot;:&quot;Herman Melville&quot;,&quot;country&quot;:&quot;United States&quot;,&quot;imageLink&quot;:&quot;images/moby-dick.jpg&quot;,&quot;language&quot;:&quot;English&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Moby-Dick&quot;,&quot;pages&quot;:378,&quot;title&quot;:&quot;Moby Dick&quot;,&quot;year&quot;:1851},{&quot;author&quot;:&quot;Michel de Montaigne&quot;,&quot;country&quot;:&quot;France&quot;,&quot;imageLink&quot;:&quot;images/essais.jpg&quot;,&quot;language&quot;:&quot;French&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Essays_(Montaigne)&quot;,&quot;pages&quot;:404,&quot;title&quot;:&quot;Essays&quot;,&quot;year&quot;:1595},{&quot;author&quot;:&quot;Elsa Morante&quot;,&quot;country&quot;:&quot;Italy&quot;,&quot;imageLink&quot;:&quot;images/history.jpg&quot;,&quot;language&quot;:&quot;Italian&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/History_(novel)&quot;,&quot;pages&quot;:600,&quot;title&quot;:&quot;History&quot;,&quot;year&quot;:1974},{&quot;author&quot;:&quot;Toni Morrison&quot;,&quot;country&quot;:&quot;United States&quot;,&quot;imageLink&quot;:&quot;images/beloved.jpg&quot;,&quot;language&quot;:&quot;English&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Beloved_(novel)&quot;,&quot;pages&quot;:321,&quot;title&quot;:&quot;Beloved&quot;,&quot;year&quot;:1987},{&quot;author&quot;:&quot;Murasaki Shikibu&quot;,&quot;country&quot;:&quot;Japan&quot;,&quot;imageLink&quot;:&quot;images/the-tale-of-genji.jpg&quot;,&quot;language&quot;:&quot;Japanese&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/The_Tale_of_Genji&quot;,&quot;pages&quot;:1360,&quot;title&quot;:&quot;The Tale of Genji&quot;,&quot;year&quot;:1006},{&quot;author&quot;:&quot;Robert Musil&quot;,&quot;country&quot;:&quot;Austria&quot;,&quot;imageLink&quot;:&quot;images/the-man-without-qualities.jpg&quot;,&quot;language&quot;:&quot;German&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/The_Man_Without_Qualities&quot;,&quot;pages&quot;:365,&quot;title&quot;:&quot;The Man Without Qualities&quot;,&quot;year&quot;:1931},{&quot;author&quot;:&quot;Vladimir Nabokov&quot;,&quot;country&quot;:&quot;Russia/United States&quot;,&quot;imageLink&quot;:&quot;images/lolita.jpg&quot;,&quot;language&quot;:&quot;English&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Lolita&quot;,&quot;pages&quot;:317,&quot;title&quot;:&quot;Lolita&quot;,&quot;year&quot;:1955},{&quot;author&quot;:&quot;George Orwell&quot;,&quot;country&quot;:&quot;United Kingdom&quot;,&quot;imageLink&quot;:&quot;images/nineteen-eighty-four.jpg&quot;,&quot;language&quot;:&quot;English&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Nineteen_Eighty-Four&quot;,&quot;pages&quot;:272,&quot;title&quot;:&quot;Nineteen Eighty-Four&quot;,&quot;year&quot;:1949},{&quot;author&quot;:&quot;Ovid&quot;,&quot;country&quot;:&quot;Roman Empire&quot;,&quot;imageLink&quot;:&quot;images/the-metamorphoses-of-ovid.jpg&quot;,&quot;language&quot;:&quot;Classical Latin&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Metamorphoses&quot;,&quot;pages&quot;:576,&quot;title&quot;:&quot;Metamorphoses&quot;,&quot;year&quot;:100},{&quot;author&quot;:&quot;Fernando Pessoa&quot;,&quot;country&quot;:&quot;Portugal&quot;,&quot;imageLink&quot;:&quot;images/the-book-of-disquiet.jpg&quot;,&quot;language&quot;:&quot;Portuguese&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/The_Book_of_Disquiet&quot;,&quot;pages&quot;:272,&quot;title&quot;:&quot;The Book of Disquiet&quot;,&quot;year&quot;:1928},{&quot;author&quot;:&quot;Edgar Allan Poe&quot;,&quot;country&quot;:&quot;United States&quot;,&quot;imageLink&quot;:&quot;images/tales-and-poems-of-edgar-allan-poe.jpg&quot;,&quot;language&quot;:&quot;English&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Edgar_Allan_Poe_bibliography#Tales&quot;,&quot;pages&quot;:842,&quot;title&quot;:&quot;Tales&quot;,&quot;year&quot;:1950},{&quot;author&quot;:&quot;Marcel Proust&quot;,&quot;country&quot;:&quot;France&quot;,&quot;imageLink&quot;:&quot;images/a-la-recherche-du-temps-perdu.jpg&quot;,&quot;language&quot;:&quot;French&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/In_Search_of_Lost_Time&quot;,&quot;pages&quot;:2408,&quot;title&quot;:&quot;In Search of Lost Time&quot;,&quot;year&quot;:1920},{&quot;author&quot;:&quot;François Rabelais&quot;,&quot;country&quot;:&quot;France&quot;,&quot;imageLink&quot;:&quot;images/gargantua-and-pantagruel.jpg&quot;,&quot;language&quot;:&quot;French&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Gargantua_and_Pantagruel&quot;,&quot;pages&quot;:623,&quot;title&quot;:&quot;Gargantua and Pantagruel&quot;,&quot;year&quot;:1533},{&quot;author&quot;:&quot;Juan Rulfo&quot;,&quot;country&quot;:&quot;Mexico&quot;,&quot;imageLink&quot;:&quot;images/pedro-paramo.jpg&quot;,&quot;language&quot;:&quot;Spanish&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Pedro_P%C3%A1ramo&quot;,&quot;pages&quot;:124,&quot;title&quot;:&quot;Pedro Páramo&quot;,&quot;year&quot;:1955},{&quot;author&quot;:&quot;Rumi&quot;,&quot;country&quot;:&quot;Sultanate of Rum&quot;,&quot;imageLink&quot;:&quot;images/the-masnavi.jpg&quot;,&quot;language&quot;:&quot;Persian&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Masnavi&quot;,&quot;pages&quot;:438,&quot;title&quot;:&quot;The Masnavi&quot;,&quot;year&quot;:1236},{&quot;author&quot;:&quot;Salman Rushdie&quot;,&quot;country&quot;:&quot;United Kingdom, India&quot;,&quot;imageLink&quot;:&quot;images/midnights-children.jpg&quot;,&quot;language&quot;:&quot;English&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Midnight%27s_Children&quot;,&quot;pages&quot;:536,&quot;title&quot;:&quot;Midnight&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;u0027s Children&quot;,&quot;year&quot;:1981},{&quot;author&quot;:&quot;Saadi&quot;,&quot;country&quot;:&quot;Persia, Persian Empire&quot;,&quot;imageLink&quot;:&quot;images/bostan.jpg&quot;,&quot;language&quot;:&quot;Persian&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Bustan_(book)&quot;,&quot;pages&quot;:298,&quot;title&quot;:&quot;Bostan&quot;,&quot;year&quot;:1257},{&quot;author&quot;:&quot;Tayeb Salih&quot;,&quot;country&quot;:&quot;Sudan&quot;,&quot;imageLink&quot;:&quot;images/season-of-migration-to-the-north.jpg&quot;,&quot;language&quot;:&quot;Arabic&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Season_of_Migration_to_the_North&quot;,&quot;pages&quot;:139,&quot;title&quot;:&quot;Season of Migration to the North&quot;,&quot;year&quot;:1966},{&quot;author&quot;:&quot;José Saramago&quot;,&quot;country&quot;:&quot;Portugal&quot;,&quot;imageLink&quot;:&quot;images/blindness.jpg&quot;,&quot;language&quot;:&quot;Portuguese&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Blindness_(novel)&quot;,&quot;pages&quot;:352,&quot;title&quot;:&quot;Blindness&quot;,&quot;year&quot;:1995},{&quot;author&quot;:&quot;William Shakespeare&quot;,&quot;country&quot;:&quot;England&quot;,&quot;imageLink&quot;:&quot;images/hamlet.jpg&quot;,&quot;language&quot;:&quot;English&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Hamlet&quot;,&quot;pages&quot;:432,&quot;title&quot;:&quot;Hamlet&quot;,&quot;year&quot;:1603},{&quot;author&quot;:&quot;William Shakespeare&quot;,&quot;country&quot;:&quot;England&quot;,&quot;imageLink&quot;:&quot;images/king-lear.jpg&quot;,&quot;language&quot;:&quot;English&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/King_Lear&quot;,&quot;pages&quot;:384,&quot;title&quot;:&quot;King Lear&quot;,&quot;year&quot;:1608},{&quot;author&quot;:&quot;William Shakespeare&quot;,&quot;country&quot;:&quot;England&quot;,&quot;imageLink&quot;:&quot;images/othello.jpg&quot;,&quot;language&quot;:&quot;English&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Othello&quot;,&quot;pages&quot;:314,&quot;title&quot;:&quot;Othello&quot;,&quot;year&quot;:1609},{&quot;author&quot;:&quot;Sophocles&quot;,&quot;country&quot;:&quot;Greece&quot;,&quot;imageLink&quot;:&quot;images/oedipus-the-king.jpg&quot;,&quot;language&quot;:&quot;Greek&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Oedipus_the_King&quot;,&quot;pages&quot;:88,&quot;title&quot;:&quot;Oedipus the King&quot;,&quot;year&quot;:-430},{&quot;author&quot;:&quot;Stendhal&quot;,&quot;country&quot;:&quot;France&quot;,&quot;imageLink&quot;:&quot;images/le-rouge-et-le-noir.jpg&quot;,&quot;language&quot;:&quot;French&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/The_Red_and_the_Black&quot;,&quot;pages&quot;:576,&quot;title&quot;:&quot;The Red and the Black&quot;,&quot;year&quot;:1830},{&quot;author&quot;:&quot;Laurence Sterne&quot;,&quot;country&quot;:&quot;England&quot;,&quot;imageLink&quot;:&quot;images/the-life-and-opinions-of-tristram-shandy.jpg&quot;,&quot;language&quot;:&quot;English&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/The_Life_and_Opinions_of_Tristram_Shandy,_Gentleman&quot;,&quot;pages&quot;:640,&quot;title&quot;:&quot;The Life And Opinions of Tristram Shandy&quot;,&quot;year&quot;:1760},{&quot;author&quot;:&quot;Italo Svevo&quot;,&quot;country&quot;:&quot;Italy&quot;,&quot;imageLink&quot;:&quot;images/confessions-of-zeno.jpg&quot;,&quot;language&quot;:&quot;Italian&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Zeno%27s_Conscience&quot;,&quot;pages&quot;:412,&quot;title&quot;:&quot;Confessions of Zeno&quot;,&quot;year&quot;:1923},{&quot;author&quot;:&quot;Jonathan Swift&quot;,&quot;country&quot;:&quot;Ireland&quot;,&quot;imageLink&quot;:&quot;images/gullivers-travels.jpg&quot;,&quot;language&quot;:&quot;English&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Gulliver%27s_Travels&quot;,&quot;pages&quot;:178,&quot;title&quot;:&quot;Gulliver&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;u0027s Travels&quot;,&quot;year&quot;:1726},{&quot;author&quot;:&quot;Leo Tolstoy&quot;,&quot;country&quot;:&quot;Russia&quot;,&quot;imageLink&quot;:&quot;images/war-and-peace.jpg&quot;,&quot;language&quot;:&quot;Russian&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/War_and_Peace&quot;,&quot;pages&quot;:1296,&quot;title&quot;:&quot;War and Peace&quot;,&quot;year&quot;:1867},{&quot;author&quot;:&quot;Leo Tolstoy&quot;,&quot;country&quot;:&quot;Russia&quot;,&quot;imageLink&quot;:&quot;images/anna-karenina.jpg&quot;,&quot;language&quot;:&quot;Russian&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Anna_Karenina&quot;,&quot;pages&quot;:864,&quot;title&quot;:&quot;Anna Karenina&quot;,&quot;year&quot;:1877},{&quot;author&quot;:&quot;Leo Tolstoy&quot;,&quot;country&quot;:&quot;Russia&quot;,&quot;imageLink&quot;:&quot;images/the-death-of-ivan-ilyich.jpg&quot;,&quot;language&quot;:&quot;Russian&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/The_Death_of_Ivan_Ilyich&quot;,&quot;pages&quot;:92,&quot;title&quot;:&quot;The Death of Ivan Ilyich&quot;,&quot;year&quot;:1886},{&quot;author&quot;:&quot;Mark Twain&quot;,&quot;country&quot;:&quot;United States&quot;,&quot;imageLink&quot;:&quot;images/the-adventures-of-huckleberry-finn.jpg&quot;,&quot;language&quot;:&quot;English&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Adventures_of_Huckleberry_Finn&quot;,&quot;pages&quot;:224,&quot;title&quot;:&quot;The Adventures of Huckleberry Finn&quot;,&quot;year&quot;:1884},{&quot;author&quot;:&quot;Valmiki&quot;,&quot;country&quot;:&quot;India&quot;,&quot;imageLink&quot;:&quot;images/ramayana.jpg&quot;,&quot;language&quot;:&quot;Sanskrit&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Ramayana&quot;,&quot;pages&quot;:152,&quot;title&quot;:&quot;Ramayana&quot;,&quot;year&quot;:-450},{&quot;author&quot;:&quot;Virgil&quot;,&quot;country&quot;:&quot;Roman Empire&quot;,&quot;imageLink&quot;:&quot;images/the-aeneid.jpg&quot;,&quot;language&quot;:&quot;Classical Latin&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Aeneid&quot;,&quot;pages&quot;:442,&quot;title&quot;:&quot;The Aeneid&quot;,&quot;year&quot;:-23},{&quot;author&quot;:&quot;Vyasa&quot;,&quot;country&quot;:&quot;India&quot;,&quot;imageLink&quot;:&quot;images/the-mahab-harata.jpg&quot;,&quot;language&quot;:&quot;Sanskrit&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Mahabharata&quot;,&quot;pages&quot;:276,&quot;title&quot;:&quot;Mahabharata&quot;,&quot;year&quot;:-700},{&quot;author&quot;:&quot;Walt Whitman&quot;,&quot;country&quot;:&quot;United States&quot;,&quot;imageLink&quot;:&quot;images/leaves-of-grass.jpg&quot;,&quot;language&quot;:&quot;English&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Leaves_of_Grass&quot;,&quot;pages&quot;:152,&quot;title&quot;:&quot;Leaves of Grass&quot;,&quot;year&quot;:1855},{&quot;author&quot;:&quot;Virginia Woolf&quot;,&quot;country&quot;:&quot;United Kingdom&quot;,&quot;imageLink&quot;:&quot;images/mrs-dalloway.jpg&quot;,&quot;language&quot;:&quot;English&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Mrs_Dalloway&quot;,&quot;pages&quot;:216,&quot;title&quot;:&quot;Mrs Dalloway&quot;,&quot;year&quot;:1925},{&quot;author&quot;:&quot;Virginia Woolf&quot;,&quot;country&quot;:&quot;United Kingdom&quot;,&quot;imageLink&quot;:&quot;images/to-the-lighthouse.jpg&quot;,&quot;language&quot;:&quot;English&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/To_the_Lighthouse&quot;,&quot;pages&quot;:209,&quot;title&quot;:&quot;To the Lighthouse&quot;,&quot;year&quot;:1927},{&quot;author&quot;:&quot;Marguerite Yourcenar&quot;,&quot;country&quot;:&quot;France/Belgium&quot;,&quot;imageLink&quot;:&quot;images/memoirs-of-hadrian.jpg&quot;,&quot;language&quot;:&quot;French&quot;,&quot;link&quot;:&quot;https://en.wikipedia.org/wiki/Memoirs_of_Hadrian&quot;,&quot;pages&quot;:408,&quot;title&quot;:&quot;Memoirs of Hadrian&quot;,&quot;year&quot;:1951}]&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We need to parse this data as JSON so JavaScript can process it.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;my_big_json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;my_big_json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;my_big_json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; -&amp;gt; &lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;my_big_json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To ensure that the loading process was successful, I’m iterating over the JSON dataset and printing each entry. You should see the following in your console log.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;https://www.ayush.nz/static/images/img-normal/2021-11-01-using-lunr-js-to-create-client-side-website-search-index-dataset.png&quot; width=&quot;1024&quot; alt=&quot;Loading Lunr search dataset into JSON in JavaScript.&quot; /&gt;
    &lt;figcaption&gt;&lt;a href=&quot;https://www.ayush.nz/static/images/img-normal/2021-11-01-using-lunr-js-to-create-client-side-website-search-index-dataset.png&quot; target=&quot;_blank&quot;&gt;&lt;i class=&quot;fas fa-search-plus me-1&quot;&gt;&lt;/i&gt;Enlarge&lt;/a&gt; — Loading Lunr search dataset into JSON in JavaScript.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;building-the-search-index&quot;&gt;Building the search index&lt;/h3&gt;

&lt;p&gt;Now we will build the search index in a variable called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;idx&lt;/code&gt; using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lunr()&lt;/code&gt; function. This step requires 3 things:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Lunr returns a document reference for every document that matches a search query. And we need to tell Lunr which field in our dataset should be the reference. Usually this reference is a numeric ID unique to every document. Since our dataset does not contain such a field I’ll use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;link&lt;/code&gt; field as the reference field.&lt;/li&gt;
  &lt;li&gt;Lunr also requires the list of fields which should be part of the search index. For this example, I want to search on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;author&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;title&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;country&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Lastly, Lunr requires our dataset… the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;my_big_json&lt;/code&gt; variable.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We can now call the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lunr()&lt;/code&gt; function to build our search index &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;idx&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lunr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
 
    &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;country&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;my_big_json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If there were no errors in your browser’s console log then our index built was successful. Let’s take it for a test drive.&lt;/p&gt;

&lt;h3 id=&quot;looking-for-books-in-our-dataset-with-lunr&quot;&gt;Looking for books in our dataset with Lunr&lt;/h3&gt;

&lt;p&gt;Let’s first see if there are any documents containing the word &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;adventures&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;results&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;adventures&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Results: &lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;results&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We should see the following in our console log:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Results:  1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Alright. So there is 1 document that matches the search query &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;adventures&lt;/code&gt;. But how do we display it?&lt;/p&gt;

&lt;p&gt;As I mentioned before, Lunr returns the reference of the matching document. But not the document itself. Meaning if we print &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;results&lt;/code&gt; we’ll see the following.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;https://www.ayush.nz/static/images/img-normal/2021-11-01-using-lunr-js-to-create-client-side-website-search-index-search.png&quot; width=&quot;1024&quot; alt=&quot;Searching for the best books of all time using Loading Lunr search.&quot; /&gt;
    &lt;figcaption&gt;&lt;a href=&quot;https://www.ayush.nz/static/images/img-normal/2021-11-01-using-lunr-js-to-create-client-side-website-search-index-search.png&quot; target=&quot;_blank&quot;&gt;&lt;i class=&quot;fas fa-search-plus me-1&quot;&gt;&lt;/i&gt;Enlarge&lt;/a&gt; — Searching for the best books of all time using Loading Lunr search.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Here &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;results&lt;/code&gt; contains an array of objects with a single element. And that element’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ref&lt;/code&gt; field contains a value. This is because we used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;link&lt;/code&gt; field as the reference. To show the full document, we’ll need to work a bit harder.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;results&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;adventures&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Results: &lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;results&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;results_full&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;results&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;my_big_json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;link&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;})[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;results_full&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;In the above code, we’re iterating through the results using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map()&lt;/code&gt; function. We then find documents in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;my_big_json&lt;/code&gt; for all the references in the result set. So &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;results_full&lt;/code&gt; contains the complete search results.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Mark Twain&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;country&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;United States&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;imageLink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;images/the-adventures-of-huckleberry-finn.jpg&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;…&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s repeat the search for the keyword &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;india&lt;/code&gt;. The code is as follows.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;results&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;india&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Results: &lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;results&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;results_full&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;results&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;my_big_json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;link&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;})[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;results_full&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And the results are:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Results:  4
0: Object &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; author: &lt;span class=&quot;s2&quot;&gt;&quot;Kālidāsa&quot;&lt;/span&gt;, country: &lt;span class=&quot;s2&quot;&gt;&quot;India&quot;&lt;/span&gt;, imageLink: &lt;span class=&quot;s2&quot;&gt;&quot;images/the-recognition-of-shakuntala.jpg&quot;&lt;/span&gt;, … &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
1: Object &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; author: &lt;span class=&quot;s2&quot;&gt;&quot;Valmiki&quot;&lt;/span&gt;, country: &lt;span class=&quot;s2&quot;&gt;&quot;India&quot;&lt;/span&gt;, imageLink: &lt;span class=&quot;s2&quot;&gt;&quot;images/ramayana.jpg&quot;&lt;/span&gt;, … &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
2: Object &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; author: &lt;span class=&quot;s2&quot;&gt;&quot;Vyasa&quot;&lt;/span&gt;, country: &lt;span class=&quot;s2&quot;&gt;&quot;India&quot;&lt;/span&gt;, imageLink: &lt;span class=&quot;s2&quot;&gt;&quot;images/the-mahab-harata.jpg&quot;&lt;/span&gt;, … &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
3: Object &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; author: &lt;span class=&quot;s2&quot;&gt;&quot;Salman Rushdie&quot;&lt;/span&gt;, country: &lt;span class=&quot;s2&quot;&gt;&quot;United Kingdom, India&quot;&lt;/span&gt;, imageLink: &lt;span class=&quot;s2&quot;&gt;&quot;images/midnights-children.jpg&quot;&lt;/span&gt;, … &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And it’s that simple! Adding search for any array of JSON objects only requires 5 easy steps:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Lunr.js&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Identify the reference fields and the search fields.&lt;/li&gt;
  &lt;li&gt;Build the search index by iterating over the dataset.&lt;/li&gt;
  &lt;li&gt;Call the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;search()&lt;/code&gt; method to search the index and return the matching references.&lt;/li&gt;
  &lt;li&gt;And finally, retrieve the documents for the matching references.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;prebuilding-lunrjss-search-index&quot;&gt;Prebuilding Lunr.js’s search index&lt;/h2&gt;

&lt;p&gt;You may have noticed that the search index takes some time to build on every page refresh. The time may be imperceptible right now. But it won’t be when &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lunr.html&lt;/code&gt; is on a remote server. Lunr allows &lt;a href=&quot;https://lunrjs.com/guides/index_prebuilding.html&quot;&gt;pre-building the search index&lt;/a&gt; to make the search more responsive.&lt;/p&gt;

&lt;p&gt;There are 2 ways of pre-building the index. The first method is to serialise the index after building it. Since we’ve already created the index in our tutorial we’ll use this method.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;serializedIdx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;serializedIdx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We would then replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;my_big_json&lt;/code&gt; with the serialised string. Our index load command would also change.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;my_big_json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lunr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;serializedIdx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;a href=&quot;https://lunrjs.com/guides/index_prebuilding.html&quot;&gt;second method&lt;/a&gt; involves calling the above commands in CLI. This method is great for running in a CI/CD pipeline. I’ll talk about this method more later on.&lt;/p&gt;

&lt;h2 id=&quot;lunrjs-search-tips-and-tricks&quot;&gt;Lunr.js search tips and tricks&lt;/h2&gt;
&lt;p&gt;Web search engines like &lt;a href=&quot;https://duckduckgo.com&quot;&gt;DuckDuckGo&lt;/a&gt; provide a way to prioritise search terms. For example, a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+&lt;/code&gt; sign will include a search term and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-&lt;/code&gt; will exclude it.&lt;/p&gt;

&lt;p&gt;Lunr provides similar features to prioritise search terms or search fields. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+&lt;/code&gt; will include a search term and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-&lt;/code&gt; will exclude it. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~&lt;/code&gt; symbol allows fuzzy matching. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;^&lt;/code&gt; symbol allows weighting search terms. And the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:&lt;/code&gt; symbol allows search for specific fields.&lt;/p&gt;

&lt;p&gt;For example, to find the best books published in England but not by Shakespeare, our Lunr query will be:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;results&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;country:England -author:Shakespeare&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This gives me 2 results (truncated):&lt;/p&gt;
&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Geoffrey Chaucer&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;country&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;England&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;imageLink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;images/the-canterbury-tales.jpg&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;language&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;English&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https://en.wikipedia.org/wiki/The_Canterbury_Tales&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Laurence Sterne&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;country&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;England&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;imageLink&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;images/the-life-and-opinions-of-tristram-shandy.jpg&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;language&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;English&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https://en.wikipedia.org/wiki/The_Life_and_Opinions_of_Tristram_Shandy,_Gentleman&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;lunr-in-action-findmymastodoncom&quot;&gt;Lunr in action: findmymastodon.com!&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://findmymastodon.com&quot;&gt;findmymastodon.com&lt;/a&gt; helps users find, well, Mastodon instances. These instances often cater to specific languages, themes, and interests. And so the website needed a search engine.&lt;/p&gt;

&lt;p&gt;I started by first building the dataset using Python. The &lt;a href=&quot;https://gitlab.com/ayush-sharma/find-my-mastodon/-/blob/master/data/data-raw.json&quot;&gt;dataset&lt;/a&gt; contains metadata for thousands of Mastodon instances currently live. I then used &lt;a href=&quot;https://gitlab.com/ayush-sharma/find-my-mastodon/-/blob/master/README.md#building-lunr-index&quot;&gt;Node to create a serialised Lunr index&lt;/a&gt; from the original JSON. This &lt;a href=&quot;https://gitlab.com/ayush-sharma/find-my-mastodon/-/blob/master/data/search-index.js&quot;&gt;Lunr index&lt;/a&gt; is then loaded as a static asset. Search executes against this index and the Mastodon instances, if found, display on the search page. You can browse the &lt;a href=&quot;https://gitlab.com/ayush-sharma/find-my-mastodon/-/blob/master/static/js/script-search.js&quot;&gt;JavaScript source code here&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Lunr can prove to be a useful ally in webdev. It’s especially great for static websites which don’t rely on an active backend. Support for pre-built indices using CLI is a huge performance bonus. This allows using CI/CD to fetch data from a remote backend and create the index. Meaning the backend no longer needs to remain active to serve search requests.&lt;/p&gt;

&lt;p&gt;Client-side search is a new thing for me. I’ve been a DevOps/cloud guy for a long time and I’m eager to find out how this can improve security and optimise costs. Web browsers are becoming more sophisticated every day. Letting them handle the heavy-lifting for search might grant performance improvements, improved user experiences, and lower cloud costs.&lt;/p&gt;

&lt;p&gt;Thanks for reading this how-to, and I hope it sparked some creative ideas in your head. Happy coding :)&lt;/p&gt;

            </description>
        </item>
        
        <item>
            <title>Creating light and dark themes for websites the right way using prefers-color-scheme</title>
            <pubDate>Oct 10, 2021</pubDate>
            <link>https://www.ayush.nz/2021/10/creating-light-and-dark-themes-for-websites-correctly-using-prefers-color-scheme</link>
            <guid isPermaLink="true">https://www.ayush.nz/2021/10/creating-light-and-dark-themes-for-websites-correctly-using-prefers-color-scheme</guid>
            
            
            <description>
                &lt;p&gt;You’re likely already familiar with media queries. They’re in widespread use for making websites responsive. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;width&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;height&lt;/code&gt; properties contain the viewport’s dimensions. We then use CSS to render different layouts at different dimensions.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://drafts.csswg.org/mediaqueries-5/#prefers-color-scheme&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prefers-color-scheme&lt;/code&gt; media query&lt;/a&gt; works the same way. The user can configure their operating system to use a light or dark theme. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prefers-color-scheme&lt;/code&gt; contains that value. The value is either &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;light&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dark&lt;/code&gt;, though the W3C spec states that it might support future values like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sepia&lt;/code&gt;. We specify different values of CSS variables for both modes and let the user’s OS decide.&lt;/p&gt;

&lt;h2 id=&quot;the-prefers-color-scheme-media-queries&quot;&gt;The prefers-color-scheme media queries&lt;/h2&gt;

&lt;p&gt;The two variations of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prefers-color-scheme&lt;/code&gt; media query are:&lt;/p&gt;

&lt;div class=&quot;language-scss highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cm&quot;&gt;/* Light mode */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefers-color-scheme&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;light&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;--body-bg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;#FFFFFF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;--body-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;#000000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;cm&quot;&gt;/* Dark mode */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefers-color-scheme&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;--body-bg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;#000000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;--body-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;#FFFFFF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the above CSS, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--body-bg&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--body-color&lt;/code&gt; are &lt;a href=&quot;https://www.w3.org/TR/css-variables-1/&quot;&gt;CSS variables&lt;/a&gt;. As you can see, they contain different values for both modes. In the light theme, we’re setting a white background with black text. In dark theme, we’re setting black background with white text.&lt;/p&gt;

&lt;p&gt;Since the &lt;a href=&quot;https://drafts.csswg.org/mediaqueries-5/#prefers-color-scheme&quot;&gt;spec&lt;/a&gt; says that W3C might introduce future values, it makes sense to convert our CSS into a boolean.&lt;/p&gt;

&lt;div class=&quot;language-scss highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cm&quot;&gt;/* Light mode */&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;--body-bg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;#FFFFFF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;--body-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;#000000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;cm&quot;&gt;/* Dark mode */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefers-color-scheme&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;--body-bg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;#000000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;--body-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;#FFFFFF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the above code, we’re defining a light theme by default, and converting to the dark theme if the media query is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dark&lt;/code&gt;. This way any future values added to the media query will set the light theme by default.&lt;/p&gt;

&lt;h2 id=&quot;using-the-css-variables&quot;&gt;Using the CSS variables&lt;/h2&gt;

&lt;p&gt;Now that we have different values for different themes, we need to actually use it to style our page.&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;--body-bg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;--body-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;a href=&quot;https://www.w3.org/TR/css-variables-1/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;var()&lt;/code&gt; syntax&lt;/a&gt; is how CSS uses variables. In the above code, we’re saying that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;background&lt;/code&gt; should be set to the value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--body-bg&lt;/code&gt;. And &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;color&lt;/code&gt; should be set to the value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--body-color&lt;/code&gt;. Note that the values of these variables are coming from our media query. Meaning that our background and foreground colour changes based on the OS’s setting! &lt;strong&gt;This is the real power of the media query: providing a consistent user experience from OS to the web page.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you go to &lt;a href=&quot;https://findmymastodon.com&quot;&gt;findmymastodon.com&lt;/a&gt; and switch you OS’s theme you’ll see the transition from one theme to another. The video below shows the smooth transition from light mode to dark mode on Mac OS X.&lt;/p&gt;

&lt;figure class=&quot;figure&quot;&gt;
&lt;video width=&quot;100%&quot; preload=&quot;auto&quot; muted=&quot;&quot; controls=&quot;controls&quot;&gt;
&lt;source src=&quot;https://www.ayush.nz/static/videos/2021-10-10-creating-light-and-dark-themes-for-websites-correctly-using-prefers-color-scheme-480.webm&quot; type=&quot;video/webm&quot; /&gt;
Your browser does not support this video.
&lt;/video&gt;
&lt;/figure&gt;

&lt;p&gt;The &lt;a href=&quot;https://drafts.csswg.org/mediaqueries-5/#prefers-color-scheme&quot;&gt;CSS WG&lt;/a&gt; website also uses the same media queries. Change your OS theme and the website will switch themes to adjust.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Notice that using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prefers-color-scheme&lt;/code&gt; is no different from using a regular programming language. We define variables whose values change based on some logic. And those variables are then used for further operations.&lt;/p&gt;

&lt;p&gt;The ability to let your website adjust to the user’s theme of choice is a great accessibility feature. And it further blurs the line between desktop and web for the benefit of the user. Latest browser versions &lt;a href=&quot;https://caniuse.com/prefers-color-scheme&quot;&gt;support &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prefers-color-scheme&lt;/code&gt;&lt;/a&gt; so you can begin experimenting today.&lt;/p&gt;

&lt;p&gt;Happy coding :)&lt;/p&gt;

            </description>
        </item>
        
    </channel>
</rss>