<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Juan Fernandes</title>
    <description>The latest articles on DEV Community by Juan Fernandes (@juanfernandes).</description>
    <link>https://dev.to/juanfernandes</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F5019%2Fb6277fdb-5a01-4a9e-8fd0-ee1f1ef48e2f.jpg</url>
      <title>DEV Community: Juan Fernandes</title>
      <link>https://dev.to/juanfernandes</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/juanfernandes"/>
    <language>en</language>
    <item>
      <title>Building BKMRKS | A Lightweight Bookmarks Manager Project</title>
      <dc:creator>Juan Fernandes</dc:creator>
      <pubDate>Fri, 03 Oct 2025 13:00:00 +0000</pubDate>
      <link>https://dev.to/juanfernandes/building-bkmrks-a-lightweight-bookmarks-manager-project-97l</link>
      <guid>https://dev.to/juanfernandes/building-bkmrks-a-lightweight-bookmarks-manager-project-97l</guid>
      <description>&lt;p&gt;I’ve always relied on bookmarks to save useful links — design systems, dev docs, tools, inspiration, articles I want to revisit later. The problem is: browser bookmarks quickly become cluttered, messy, and difficult to manage across devices. I wanted something cleaner, more organised, and under my control.&lt;/p&gt;

&lt;p&gt;So I decided to build my own solution: BKMRKS — a minimal, full-featured bookmark manager that runs anywhere, needs no database, and can be deployed on simple shared hosting.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Starting Point
&lt;/h2&gt;

&lt;p&gt;Originally, I experimented with Supabase as the backend for authentication, storage, and APIs. While it worked, it quickly felt like overkill. I didn’t want to depend on a hosted service, worry about migrations, or deal with vendor lock-in.&lt;/p&gt;

&lt;p&gt;I asked myself: “Do I really need a database at all?”&lt;/p&gt;

&lt;p&gt;The answer was no. For my use case — static bookmark storage, tagging, filtering, and importing/exporting JSON — a simple file-based approach was enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Approach
&lt;/h2&gt;

&lt;p&gt;I set out to build BKMRKS as a static-first app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Frontend in React: Clean UI, fast search, category filtering, and tagging.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Data as JSON: Bookmarks live in a simple .json file — human-readable, portable, and easy to back up.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Import/Export: Users can upload/export their own bookmarks as JSON.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deploy Anywhere: Works on Netlify, Vercel, or even plain old shared hosting. No database required.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This decision massively simplified the architecture. Instead of a stack with a database, auth, and server-side code, BKMRKS is essentially just a React app plus a JSON file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;p&gt;Despite its simplicity, BKMRKS is designed to feel complete:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Search – instantly find bookmarks by title, tag, or description.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Categories – group links under T-shirts, Hoodies... (just kidding 😅) — I mean Work, Design, Dev Tools, Articles, etc.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tagging – flexible organisation across categories.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Import/Export – add bookmarks from your browser, or back them up in seconds.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Static Storage – everything is in JSON. No logins, no databases, just pure portability.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;Dropping Supabase was the right move. I didn’t need realtime sync, row-level security, or a hosted PostgreSQL instance for something that’s essentially a personal library.&lt;/p&gt;

&lt;p&gt;By keeping things static:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Setup is easier — no config, no environment variables.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Maintenance is zero — it just works on shared hosting.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Performance is great — no server round-trips, everything is local.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s a reminder that not every project needs a “modern stack” with APIs and databases. Sometimes, plain JSON files do the job.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Next?
&lt;/h2&gt;

&lt;p&gt;I want to expand BKMRKS with a few ideas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Better UI polish — more drag-and-drop, cleaner category browsing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Optional cloud sync — without tying users to a single provider.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Public sharing — the ability to make collections of bookmarks public.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For now though, it’s already doing what I need: a clean, no-frills way to manage my links across devices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Building BKMRKS reminded me why I enjoy side projects: solving my own problems in the simplest way possible. It’s lightweight, portable, and future-proof — no SaaS dependencies, just a little React app and a JSON file.&lt;/p&gt;

&lt;p&gt;Sometimes, that’s all you need.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>showdev</category>
      <category>sideprojects</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Automating my links list</title>
      <dc:creator>Juan Fernandes</dc:creator>
      <pubDate>Wed, 20 Aug 2025 10:00:00 +0000</pubDate>
      <link>https://dev.to/juanfernandes/automating-my-links-list-57b9</link>
      <guid>https://dev.to/juanfernandes/automating-my-links-list-57b9</guid>
      <description>&lt;p&gt;How I automated my Instapaper “liked” articles into a clean, sorted JSON feed for my 11ty site — with date normalization, HTML stripping, and deduplication, all running daily via GitHub Actions.&lt;/p&gt;

&lt;p&gt;I keep a running list of articles I’ve read and liked — partly for reference, partly for sharing.&lt;br&gt;&lt;br&gt;
For years, I used &lt;a href="https://www.instapaper.com/" rel="noopener noreferrer"&gt;Instapaper&lt;/a&gt; for saving articles, and IFTTT to log my “liked” ones into Airtable.&lt;/p&gt;

&lt;p&gt;But getting them onto &lt;a href="https://juanfernandes.uk/links" rel="noopener noreferrer"&gt;juanfernandes.uk/links&lt;/a&gt; meant &lt;strong&gt;manually&lt;/strong&gt; updating a JSON file. Not exactly friction-free.&lt;/p&gt;

&lt;p&gt;So I automated the whole thing. And over time, I’ve refined it to handle &lt;strong&gt;dates properly&lt;/strong&gt; , &lt;strong&gt;avoid duplicates&lt;/strong&gt; , and &lt;strong&gt;keep the list sorted&lt;/strong&gt; — no manual edits, no mess.&lt;/p&gt;


&lt;h3&gt;
  
  
  ✅ The stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Instapaper RSS feed&lt;/strong&gt; – for fetching my recently liked articles&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A custom Node.js script&lt;/strong&gt; – to grab new links, clean them, and update JSON&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Actions&lt;/strong&gt; – to run the script and rebuild my site daily&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;11ty&lt;/strong&gt; – to display the links with pagination&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  Step 1: The Instapaper RSS feed
&lt;/h3&gt;

&lt;p&gt;Instapaper gives you a feed of your “liked” articles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://www.instapaper.com/starred/rss/[user_id]/[token]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It only shows your 10 most recent liked items, so the key is &lt;strong&gt;appending new ones without losing older ones&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 2: Writing to &lt;code&gt;links.raw.json&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Instead of saving directly to &lt;code&gt;links.json&lt;/code&gt; (which Eleventy uses for pagination), the script now writes to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/_data/links.raw.json

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file is my &lt;strong&gt;raw archive&lt;/strong&gt; of everything I’ve ever liked on Instapaper.&lt;/p&gt;

&lt;p&gt;Why? Because Eleventy treats &lt;code&gt;.js&lt;/code&gt; and &lt;code&gt;.json&lt;/code&gt; files in &lt;code&gt;_data&lt;/code&gt; differently — having a &lt;code&gt;.js&lt;/code&gt; file for &lt;code&gt;links&lt;/code&gt; means I can read, sort, and clean the raw JSON every time before it’s passed to the templates.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 3: The append script (&lt;code&gt;appendToJSON.js&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;The script does the heavy lifting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fetches&lt;/strong&gt; the Instapaper RSS feed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Normalizes dates&lt;/strong&gt; → full ISO 8601 format
(&lt;code&gt;2025-07-28T18:53:22.000Z&lt;/code&gt; instead of &lt;code&gt;2025-07-28&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strips HTML from titles&lt;/strong&gt; , so tags like &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; don’t show in headings&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keeps HTML in content&lt;/strong&gt; if I want formatted excerpts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;De-dupes&lt;/strong&gt; by URL (latest version wins)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sorts newest → oldest&lt;/strong&gt; before saving&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s a trimmed example of the mapping logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function stripHtml(str) { return str ? str.replace(/&amp;lt;\/?[^&amp;gt;]+(&amp;gt;|$)/g, "") : "";}function toIso(val) { if (!val) return new Date().toISOString(); const t = Date.parse(val); return Number.isNaN(t) ? new Date().toISOString() : new Date(t).toISOString();}// Map RSS itemsconst mapped = feed.items.map(item =&amp;gt; ({ title: stripHtml(item.title || ""), url: item.link?.trim(), content: item["content:encoded"] || item.content || "", date: toIso(item.pubDate || item.isoDate)}));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 4: Sorting in Eleventy
&lt;/h3&gt;

&lt;p&gt;Now that &lt;code&gt;links.raw.json&lt;/code&gt; is clean, &lt;code&gt;src/_data/links.js&lt;/code&gt; becomes the single source of truth for Eleventy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const fs = require("fs");const path = require("path");module.exports = () =&amp;gt; { const raw = JSON.parse(fs.readFileSync(path.join(__dirname, "links.raw.json"), "utf8")); return raw.sort((a, b) =&amp;gt; Date.parse(b.date) - Date.parse(a.date));};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This guarantees that pagination always starts with the most recent items.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 5: Automation with GitHub Actions
&lt;/h3&gt;

&lt;p&gt;My GitHub Action now:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Runs &lt;code&gt;appendToJSON.js&lt;/code&gt; to update &lt;code&gt;links.raw.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Builds the site with Eleventy&lt;/li&gt;
&lt;li&gt;Deploys the updated site&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That means every day my “Read Articles” page refreshes automatically, and it’s always:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Up to date&lt;/li&gt;
&lt;li&gt;Properly sorted&lt;/li&gt;
&lt;li&gt;Free from HTML junk in titles&lt;/li&gt;
&lt;li&gt;Duplicate-free&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  The result
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://juanfernandes.uk/links" rel="noopener noreferrer"&gt;juanfernandes.uk/links&lt;/a&gt; is now fully automated and clean.&lt;br&gt;&lt;br&gt;
No more manual edits, no more date format headaches, and no need to check if something’s already been added.&lt;/p&gt;

&lt;p&gt;It just works. Every day.&lt;/p&gt;

&lt;p&gt;If you want to set up something similar, the full append script is on my &lt;a href="https://github.com/juanfernandes/juanfernandes-uk" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; — or drop me a message and I can help adapt it for your setup.&lt;/p&gt;

</description>
      <category>automation</category>
      <category>github</category>
      <category>productivity</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Working an inside IR35 contract</title>
      <dc:creator>Juan Fernandes</dc:creator>
      <pubDate>Mon, 26 Apr 2021 23:00:00 +0000</pubDate>
      <link>https://dev.to/juanfernandes/working-an-inside-ir35-contract-3389</link>
      <guid>https://dev.to/juanfernandes/working-an-inside-ir35-contract-3389</guid>
      <description>&lt;h2&gt;
  
  
  What is IR35?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;IR35 is the name given to a tax legislation that is aimed at identifying individuals who are avoiding paying the tax that they should be. The IR35 legislation specifically challenges those people who supply their services to clients via their own company, often known as a ‘personal service company’, be it a limited company or a limited liability partnership, who, in the ever-watchful eyes of HMRC, should be classed as ‘disguised employees’. This means that HMRC do not recognise the contractor in question as ‘self-employed’ from a taxation perspective and therefore they should be taxed the same way that a permanent employee should be, thus falling insider what is called IR35.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://www.contractorweekly.com/ir35/what-is-ir35/" rel="noopener noreferrer"&gt;Contractor Weekly&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My experience
&lt;/h2&gt;

&lt;p&gt;To be fair, I've only had one contract that was inside IR35 and after a brief &lt;a href="https://twitter.com/_worknotes/status/1380147375285678081" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; conversation I decided to write a blog post about my experienvce working an inside IR35 contract and why I decided it's not for me and pushed me back to peermanent employment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Umbrella companies
&lt;/h3&gt;

&lt;p&gt;The biggest issue I had was finding a good umbrella company - you have to do your research, including looking at companies house and checking their accounts. If they haven't updated their accounts in a while, then something may not be right. Remember they get your money take a cut and then pay you - if they are having money problems, they may not be able to pay.&lt;/p&gt;

&lt;p&gt;Also, checkout umbrella company reviews on contractor websites. This article on IT Contracting - &lt;a href="https://www.itcontracting.com/umbrella-company-reviews-trust/" rel="noopener noreferrer"&gt;Umbrella company reviews – can they be trusted?&lt;/a&gt; has a lot more information on what you should look at when researching umbrella companies.&lt;/p&gt;

&lt;p&gt;You will need to find contracts that pay at least £100 per day more than your usual day rate if you want your take-home pay to be the same as an outside IR35 contract. When looking for an umbrella company, one of the main questions you'll want to get an answer to is - what is the take-home pay?&lt;/p&gt;

&lt;p&gt;Not all recruitment agencies will let you just choose any Umbrella company - it will likely have to be one from a list of approved companies. I had a list of twenty to choose from - but the one that was highly recommended to me was not on that list.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pensions
&lt;/h3&gt;

&lt;p&gt;Umbrella companies have to automatically enrol you into a pension after three months - not something I needed as I already had a private one - but they still did it and I had to cancel it and chase get back two payments back.&lt;/p&gt;

&lt;p&gt;Some umbrella companies allow you to pay your pension contributions into an existing pension, including a SIPP (Self-invested Personal Pension) if you have one - you need to check if they do this and you need to tell them before they auto-enrol you onto their one. If you have a SIPP, make sure they can pay your contributions into it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Accountants
&lt;/h3&gt;

&lt;p&gt;Your accountant may charge you more to be able to help with an inside IR35 contract if they normally just deal with your business accounts and not personal - since your earnings will be paid into your account and not your business account. Mine didn't and was very helpful.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multiple employments
&lt;/h3&gt;

&lt;p&gt;You can have more than one employment - you're employed by your own limited company as well as by the umbrella company but this means you won't have a P45 to give to the umbrella company - this is fine - but you may end up on the wrong tax code or the emergency tax code - which means you'll pay a lot more tax. Speaking with my accountant it was easier to just end my employment with my own company so that I could get a P45 instead of risking it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Salary
&lt;/h3&gt;

&lt;p&gt;If you normally pay yourself a salary from your limited company - then that will need to stop if you're no longer employed by them (you), but if you choose to stay employed with your company then be aware that you'll pay more taxes because you'll be earning more.&lt;/p&gt;

&lt;p&gt;In the three months, I was in a contract - I only 4 weeks where my pay was the same. And once my tax code was changed at HMRC but no one knew why. In the 5 years, I've been self-employed that has never happened.&lt;/p&gt;

&lt;h3&gt;
  
  
  Timesheets
&lt;/h3&gt;

&lt;p&gt;I had to complete three timesheets a week - the recruitment agency, the umbrella company and one for the client.&lt;/p&gt;

&lt;h2&gt;
  
  
  Just my experience
&lt;/h2&gt;

&lt;p&gt;By the way, these are just my observations based on my short experience and research. Please speak to your accountant and/or a professional who specialises in IR35 regulation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is an inside IR35 contract right for you?
&lt;/h3&gt;

&lt;p&gt;My advice is to try it and find out for yourself. After reading this you'll have more information and hopefully know more about what to expect. It's what I decided to do, I had only heard negative things about inside IR35 contracts and with more and more contracts being inside - I needed to see what it was like for myself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not just inside IR35 contracts
&lt;/h2&gt;

&lt;p&gt;Some recruitment agencies will need proof that a contract is in fact outside the IR35 scope as stated by the client and this means you may need to pay a third party company, arranged by the recruitment agency, that will check whether the contract is or isn't outside IR35. Not sure if this is the same with all agencies, but I had to pay for this service - luckily the client was happy to pay this fee - as I wasn't happy to fork out the £80 for the privilege.&lt;/p&gt;

&lt;h2&gt;
  
  
  IR35 Insurance
&lt;/h2&gt;

&lt;p&gt;There is tax investigation insurance for IR35. It also covers your compliance with regulations relating to national insurance contributions or VAT amongst other things. However, the spotlight right now is on IR35. This is not a sponsored link - but I have Professional indemnity insurance from WithJack and I found they now do tax investigation insurance as well.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://withjack.co.uk/insurance/2020/01/27/lowdown-on-ir35-insurance/" rel="noopener noreferrer"&gt;Checkout WithJack insurance&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  That's it
&lt;/h2&gt;

&lt;p&gt;I hope you found this post useful if you are currently considering a contract inside IR35. Do let me know if you have had a different experience.&lt;/p&gt;

</description>
      <category>career</category>
      <category>developer</category>
      <category>discuss</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>How I improved my website because of Content Security Policies</title>
      <dc:creator>Juan Fernandes</dc:creator>
      <pubDate>Fri, 08 Jan 2021 23:00:00 +0000</pubDate>
      <link>https://dev.to/juanfernandes/how-i-improved-my-website-because-of-content-security-policies-26b9</link>
      <guid>https://dev.to/juanfernandes/how-i-improved-my-website-because-of-content-security-policies-26b9</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--C-qq02jd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/fx8z383cra4lrdopizqd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--C-qq02jd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/fx8z383cra4lrdopizqd.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Last month I delved into security headers including Content Security Policies and added a bunch of them to my website. I did this to learn more about website security and improve my websites' security.&lt;/p&gt;

&lt;p&gt;But I learnt that I had inadvertently broken some things on my site - this wasn't obvious because I have a cobbled together ServiceWorker which meant I didn't see the things that had stopped working - namely images and sliders.&lt;/p&gt;

&lt;p&gt;I spent a few hours applying fixes to my website to comply with the Content Security Policies I implemented a few weeks ago and at the same time improved my websites' performance and usability.&lt;/p&gt;

&lt;p&gt;CSP or Content Security Policy - are a set of rules you add to a web server to say how content should be served and from where. For example, you can choose to serve images, CSS and JS just from your domain. That means, for example, google analytics would not work and any scripts hosted elsewhere unless you specifically allow that domain.&lt;/p&gt;

&lt;p&gt;The issue I had is that its either all or nothing. So you cant have any inline JS or CSS. The JS was a quick fix - I needed to move it to a file. But I use CSS to apply background images to components via the CMS. So I needed to move that to the CSS file and use a class to apply a chosen background image.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I changed
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Problem - Inline CSS
&lt;/h3&gt;

&lt;p&gt;One of the issues I had was that all inline CSS is blocked, only linked to CSS files will be applied to my website, this meant that I had to change how I did the background images for the hero section. I was able to add the image via front matter or the CMS, which would then be added to the hero section using inline CSS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;p&gt;I now have a list of modifier classes I add to the hero section that chooses the type of background image I use. I have been using the same image for code related blog posts, for example, so it made sense to move that to a class in my main stylesheet. I can always create new ones with different background images when I need them.&lt;/p&gt;

&lt;p&gt;You can use the "inline-unsafe" CSP rule to allow inline styling, but this makes it, as the name suggests, not safe to use. I think with my limited security knowledge that if you disallow inline scripts, it would be safe to allow inline styling - I can imagine the only way for someone to change the styling of your website would be via an injected script.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem - Inline JavaScript, JS Library
&lt;/h3&gt;

&lt;p&gt;I had a testimonials slider on my homepage - it used a slider script and some custom JS to customise the slider. I started by moving the custom code from being inline into a JS file and linking to it, but I was struggling to generate a SHA (Secure Hash Algorithm) for the slider library.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;p&gt;Since I was struggling to get a SHA to work with the code for the slider, I decided to get rid of all the JavaScript, not just from the homepage, but also from the portfolio work pages.&lt;/p&gt;

&lt;p&gt;Now I show a random testimonial on the homepage, with a new one being randomly shown every time there is a new build of my website.&lt;/p&gt;

&lt;p&gt;I also removed the image slider from portfolio projects. The images now are shown in a column with project details on the right which I also applied position sticky to the project detail sidebar to it stays with the user as they scroll down to see all the project images.&lt;/p&gt;

&lt;h2&gt;
  
  
  The result?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SEu75kn0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juanfernandes/q_auto%2Cf_auto/fireworks.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SEu75kn0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juanfernandes/q_auto%2Cf_auto/fireworks.jpg" alt="Shows the results of a Chromium Lighthouse test on my website"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I enjoyed it this - now my website fully complies with very secure CSPs. It's more secure, has better performance and better for users. Win win.&lt;/p&gt;

</description>
      <category>security</category>
      <category>performance</category>
    </item>
    <item>
      <title>Add your site to the Eleventy Leaderboards</title>
      <dc:creator>Juan Fernandes</dc:creator>
      <pubDate>Wed, 09 Dec 2020 23:00:00 +0000</pubDate>
      <link>https://dev.to/juanfernandes/add-your-site-to-the-eleventy-leaderboards-4gbm</link>
      <guid>https://dev.to/juanfernandes/add-your-site-to-the-eleventy-leaderboards-4gbm</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xKkyU1ji--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juanfernandes/q_auto%2Cf_auto/eleventy-leaderboards.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xKkyU1ji--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juanfernandes/q_auto%2Cf_auto/eleventy-leaderboards.jpg" alt="Screenshot of the Eleventy Leaderboards top 5"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Eleventy (11ty) leaderboard benchmarks websites built with Eleventy over time.&lt;/p&gt;

&lt;p&gt;TL;DR - You need to add your site to the via GitHub to the eleventy site repository and create a pull request for it to be included in the leaderboards.&lt;/p&gt;




&lt;h2&gt;
  
  
  Let's get started
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Go to the eleventy GitHub repository - &lt;a href="https://github.com/11ty/11ty-website"&gt;https://github.com/11ty/11ty-website&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Navigate to &lt;code&gt;/_data/sites&lt;/code&gt; - here you will find &lt;code&gt;JSON&lt;/code&gt; files - each one represents a website built with Eleventy and ones shown on the leaderboard&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now you need to create your own &lt;code&gt;JSON&lt;/code&gt; file for your website, so click on the &lt;strong&gt;&lt;em&gt;Add file&lt;/em&gt;&lt;/strong&gt; button and then on the dropdown click on &lt;strong&gt;&lt;em&gt;Create new file&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VtrqnlTs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juanfernandes/q_auto%2Cf_auto/1-create-new-file.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VtrqnlTs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juanfernandes/q_auto%2Cf_auto/1-create-new-file.jpg" alt="Shows user selecting create new file menu item in GitHub"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enter your a file name in the &lt;strong&gt;&lt;em&gt;Name your file...&lt;/em&gt;&lt;/strong&gt; input. This is a filename, so don't use spaces or punctuation marks, but you can use hyphens (-) or underscores (_) to separate words&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use the template below with your website's details as the content of your new file:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "url": " ",
    "name": " ",
    "description": "",
    "twitter": "",
    "authoredBy": [""],
    "source_url": ""
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;url&lt;/strong&gt; : The site’s live URL&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;name&lt;/strong&gt; : Name of the site&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;description&lt;/strong&gt; : A short text description of the site&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;twitter&lt;/strong&gt; : Twitter username for the site or the site’s author.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;authoredBy&lt;/strong&gt; : An array of Twitter usernames of the site’s authors. Supplements the twitter entry. (Optional)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;source_url&lt;/strong&gt; : URL to the source code (Optional)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gGYQqsrw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juanfernandes/q_auto%2Cf_auto/2-add-your-file.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gGYQqsrw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juanfernandes/q_auto%2Cf_auto/2-add-your-file.jpg" alt="Shows new JSON file being created in GitHub"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that you're done creating the new file, you need to save it and also create a pull request - this will ask for your file to be added to the eleventy repository.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;You don't need to add anything to the optional description field&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click on the &lt;strong&gt;&lt;em&gt;Propose new file&lt;/em&gt;&lt;/strong&gt; button&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;On the next screen, you can check your changes and then click on the &lt;strong&gt;&lt;em&gt;Create pull request&lt;/em&gt;&lt;/strong&gt; button to proceed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You don't need to add a comment, but you can thank &lt;a href="https://twitter.com/zachleat"&gt;Zach&lt;/a&gt; for creating Eleventy. Then click on the &lt;strong&gt;&lt;em&gt;Create pull request&lt;/em&gt;&lt;/strong&gt; button again&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Next, GitHub will do a bunch of checks on your changes and it will tell you if there are any conflicts. Hopefully, there aren't, but you followed this exactly you shouldn't have any conflicts.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6TFkNHV0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juanfernandes/q_auto%2Cf_auto/3-final-step.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6TFkNHV0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juanfernandes/q_auto%2Cf_auto/3-final-step.jpg" alt="Shows a sucessfull pull request screen in GitHub"&gt;&lt;/a&gt;This is the result of me submitting an actual change, so I'd have a screenshot of what the final stage looks like.&lt;/p&gt;

&lt;p&gt;That's it - you can now close the page and wait. Once your pull request gets merged, your website will be included in the next round of leaderboard checks and added to the leaderboard.&lt;/p&gt;

&lt;p&gt;Keep an eye out on the &lt;a href="https://www.11ty.dev/speedlify/"&gt;Eleventy Leaderboards&lt;/a&gt; to see how your website scores.&lt;/p&gt;

&lt;p&gt;Once your website has been added to the Eleventy GitHub repository, you will also get your author page on the Eleventy website.&lt;/p&gt;

&lt;p&gt;Check out my &lt;a href="https://www.11ty.dev/authors/juanfernandes/"&gt;author page&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>eleventy</category>
      <category>ssg</category>
    </item>
    <item>
      <title>Improving Website Security</title>
      <dc:creator>Juan Fernandes</dc:creator>
      <pubDate>Fri, 20 Nov 2020 16:00:00 +0000</pubDate>
      <link>https://dev.to/juanfernandes/improving-website-security-25ol</link>
      <guid>https://dev.to/juanfernandes/improving-website-security-25ol</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IPmyKbko--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ttij8g6el5sbz2lksgr2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IPmyKbko--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ttij8g6el5sbz2lksgr2.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ever since I converted my website to Eleventy - from PHP - I have been obsessively working ton improving its performance.&lt;/p&gt;

&lt;p&gt;One well-known tool that I use for checking website performance is &lt;a href="https://www.webpagetest.org"&gt;WebPageTest.org&lt;/a&gt; and they recently added a new metric to test for - security.&lt;/p&gt;

&lt;p&gt;This new test is provided by the &lt;a href="https://snyk.io/"&gt;Snyk&lt;/a&gt;. You can sign up for a free account, give it access to your project's repository and it will actively perform security checks on your code dependencies and you'll be notified if there are any vulnerabilities.&lt;/p&gt;

&lt;p&gt;You don't need to have a Snyk account to improve your website security. You only need one if you want them to scan your website repository. 👍 But If you do, you can also use their command-line tool to check your website locally as you work on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  So why do this?
&lt;/h2&gt;

&lt;p&gt;Firstly, I didn't like seeing that red square with an E grade next to all the green checks ✅ - but also it was a good learning opportunity.&lt;/p&gt;

&lt;p&gt;Confession time - I'm not a website or server security expert. Everything I did to my website was based on what I learnt whilst researching the suggestions from Snyk and WebPageTest.&lt;/p&gt;

&lt;p&gt;Also, and most importantly to protect my website and users from hacks introduced via JavaScript vulnerabilities like cross-site scripting or compromised npm scripts and a lot more.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I do this?
&lt;/h2&gt;

&lt;p&gt;First, test your website using WebPateTest.org and see what level of security your website currently has. Then If you need to improve it, look at your security report to see what Snyk recommends you need to improve.&lt;/p&gt;

&lt;p&gt;Based on the report from Snyk, on WebPageTest I researched each of the issues it said I needed to fix. To see what you need to fix, click on the grade letter for security to be taken to your detailed report.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;These are the security issues I had to fix:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;X Content Type Options:&lt;/strong&gt; The only defined value, "nosniff", prevents Internet Explorer from MIME-sniffing a response away from the declared content-type. This also applies to Google Chrome, when downloading extensions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;X Frame Options:&lt;/strong&gt; Clickjacking protection: deny - no rendering within a frame, sameorigin - no rendering if origin mismatch, allow-from - allow from specified location, allowall - non-standard, allow from any location&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Content Security Policy:&lt;/strong&gt; A computer security standard introduced to prevent cross-site scripting (XSS), clickjacking and other code injection attacks resulting from execution of malicious content in the trusted web page context&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;X XSS Protection:&lt;/strong&gt; A Cross-site scripting filter. The HTTP X-XSS-Protection response header is a feature of Internet Explorer, Chrome and Safari that stops pages from loading when they detect reflected cross-site scripting (XSS) attacks.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Let's look at some code
&lt;/h2&gt;

&lt;p&gt;This was done specifically for my website which is hosted with Netlify, but the same principles apply to other hosts - just be aware that the way the headers code is formatted may differ.&lt;/p&gt;

&lt;p&gt;There are two ways to do this for a website hosted on netlify - you can either add it to your &lt;code&gt;netlify.toml&lt;/code&gt; config file or create a &lt;code&gt;_headers&lt;/code&gt; file.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.netlify.com/routing/headers/#syntax-for-the-netlify-configuration-file"&gt;Syntax for the config file&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.netlify.com/routing/headers/#syntax-for-the-headers-file"&gt;Syntax for the headers file&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's up to you which one you use - the difference between the two is how the headers code is formatted.&lt;/p&gt;

&lt;p&gt;I chose to add mine to the netlify config file because I already had that file and it made sense to keep all netlify related settings in the same place.&lt;/p&gt;

&lt;p&gt;Here is what my security headers look like in my netlify config file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[[headers]]
  for = "/*"
  [headers.values]
    X-Content-Type-Options = "nosniff"
    Content-Security-Policy = "default-src 'self' https://res.cloudinary.com; frame-ancestors 'none'; style-src 'self' 'unsafe-inline';"
    X-Frame-Options = "DENY"
    X-XSS-Protection = "1; mode=block"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could copy the above code and add it to your netlify config file, but it may be better if you do your research, as some of these security headers may stop scripts from third-parties working on your website.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qu1l2fYv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juanfernandes/q_auto%2Cf_auto/v1605887921/before-and-after_sbwdh3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qu1l2fYv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juanfernandes/q_auto%2Cf_auto/v1605887921/before-and-after_sbwdh3.jpg" alt="Before and after WebPageTest results"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There you have it - easy when you know how and I didn't break my site once. There are still a few more things to work on going forward, looking at the Mozilla Observatory test results for my website shows that there is still room for improvement - but I now have the basics covered and a more secure website.&lt;/p&gt;

&lt;p&gt;I hope you found this post useful. I have listed the resources I used bellow to help you make your website more secure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Learning Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection"&gt;X XSS Protection documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP"&gt;Content Security Policy documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options"&gt;X Frame Options documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options"&gt;X Content Type Options documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://notes.elmiko.dev/2019/06/27/netlify-headers.html"&gt;Netlify Headers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.keycdn.com/support/x-content-type-options"&gt;X Content Type Options on KeyCDN&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tomodwyer.com/posts/2017-08-20-improve-your-mozilla-observatory-score"&gt;Improve your Mozilla observatory score&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://infosec.mozilla.org/guidelines/web_security"&gt;Web Security - Mozilla guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tools
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://observatory.mozilla.org/"&gt;Mozilla Observatory&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://securityheaders.com/"&gt;Security Headers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ssllabs.com/ssltest/"&gt;SSL Labs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>netlify</category>
      <category>security</category>
      <category>website</category>
      <category>eleventy</category>
    </item>
    <item>
      <title>Creating a Sitemap file with Eleventy</title>
      <dc:creator>Juan Fernandes</dc:creator>
      <pubDate>Thu, 22 Oct 2020 23:00:00 +0000</pubDate>
      <link>https://dev.to/juanfernandes/creating-a-sitemap-file-with-eleventy-44dk</link>
      <guid>https://dev.to/juanfernandes/creating-a-sitemap-file-with-eleventy-44dk</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgk6oyz07jpeuq3kf2g5a.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgk6oyz07jpeuq3kf2g5a.jpg" alt="Creating a Sitemap file with Eleventy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a quick tip showing how I created a &lt;code&gt;sitemap.xml&lt;/code&gt; file using &lt;a href="https://www.11ty.dev" rel="noopener noreferrer"&gt;Eleventy&lt;/a&gt; that will automatically update itself when you create new pages.&lt;/p&gt;

&lt;p&gt;You need a sitemap to make it easier for search engines to index your website - but you can also inform them about how often pages are updated, when they were last updated and the priority level for each page.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a sitemap.xml file?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;The Sitemaps protocol allows a webmaster to inform search engines about URLs on a website that are available for crawling. A Sitemap is an XML file that lists the URLs for a site. It allows webmasters to include additional information about each URL: when it was last updated, how often it changes, and how important it is in relation to other URLs of the site.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://en.wikipedia.org/wiki/Sitemaps" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;The first thing I am doing here is excluding the sitemap file from the eleventy collection - we don't want the sitemap listing the sitemap.xml file as an entry.&lt;/p&gt;

&lt;p&gt;I then set the permalink - which is the filename we want as its what search engines look for when they visit your website.&lt;/p&gt;

&lt;p&gt;We then loop through the eleventy &lt;code&gt;collections.all&lt;/code&gt; and output each page URL wrapped in a &lt;code&gt;&amp;lt;url&amp;gt;&lt;/code&gt; tag. Inside that tag, we have the standard sitemap tags, &lt;code&gt;&amp;lt;loc&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;lastmod&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;changefreq&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;priority&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LOC is short for Location and holds the complete URL to the page&lt;/li&gt;
&lt;li&gt;LASTMOD is short for Last Modified which is the date the file was last changed&lt;/li&gt;
&lt;li&gt;CHANGEFREQ is short for Change Frequency which tells search engines how often the page changes&lt;/li&gt;
&lt;li&gt;PRIORITY tag which tells search engines the priority of the page.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Let's see the code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
eleventyExcludeFromCollections: true
permalink: sitemap.xml
---
&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;
&amp;lt;urlset
      xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
            http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"&amp;gt;
  {%- for page in collections.all %}
  &amp;lt;url&amp;gt;
    &amp;lt;loc&amp;gt;{{ site.url }}{{ page.url }}&amp;lt;/loc&amp;gt;
    &amp;lt;lastmod&amp;gt;{{ page.date | w3DateFilter }}&amp;lt;/lastmod&amp;gt;
    &amp;lt;changefreq&amp;gt;monthly&amp;lt;/changefreq&amp;gt;
    {% if page.url == '/' %}
    &amp;lt;priority&amp;gt;1.00&amp;lt;/priority&amp;gt;
    {% else %}
    &amp;lt;priority&amp;gt;0.50&amp;lt;/priority&amp;gt;
    {% endif %}
  &amp;lt;/url&amp;gt;
  {%- endfor %}
&amp;lt;/urlset&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  A couple of things to note
&lt;/h3&gt;

&lt;p&gt;I am using a filter to process and format the date - that's the w3DateFilter in the lastmod tag.&lt;/p&gt;

&lt;p&gt;Also, I'm setting the home page as having the highest priority and setting all other pages to be less of a priority. I'm not an SEO expert and so I don't know if this is the best way of doing it or not, but based on some quick research I'm convinced this approach will work as I read that if you omit the priority, then a page's priority is usually set to 0.5 by default. This will work for smaller sites, but on a large site - this may not be the best approach.&lt;/p&gt;

&lt;p&gt;Same with the Change Frequency - I'm setting all pages to have monthly set as the default for how often the page changes. I won't be doing many updates on my clients' website so I think monthly is appropriate for their website.&lt;/p&gt;

&lt;p&gt;That's it - this is enough for a small website that doesn't get updated very often, but we can make this better for larger sites with content authors - let's look into that now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Now let's make it better
&lt;/h2&gt;

&lt;p&gt;The above example works perfectly for my client and their small website. It doesn't need to be updated regularly and they don't have a lot of pages - plus they are not very technical, so asking them to specify a page priority and change frequency I know it just wouldn't work for them.&lt;/p&gt;

&lt;p&gt;But let's say you have a client website that has content authors and they are used to SEO terms, page priorities and change frequencies - let's give them the ability to add those to pages.&lt;/p&gt;

&lt;p&gt;Here we are adding it via frontmatter but this could be handled by a CMS as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
eleventyExcludeFromCollections: true
permalink: sitemap.xml
---
&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;
&amp;lt;urlset
      xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
            http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"&amp;gt;
  {%- for page in collections.all %}
  &amp;lt;url&amp;gt;
    &amp;lt;loc&amp;gt;{{ site.url }}{{ page.url }}&amp;lt;/loc&amp;gt;
    &amp;lt;lastmod&amp;gt;{{ page.date | w3DateFilter }}&amp;lt;/lastmod&amp;gt;
    &amp;lt;changefreq&amp;gt;{% if page.data.changefreq %}{{ page.data.changefreq }}{% else %}monthly{% endif %}&amp;lt;/changefreq&amp;gt;
    &amp;lt;priority&amp;gt;{% if page.data.priority %}{{ page.data.priority }}{% else %}0.5{% endif %}&amp;lt;/priority&amp;gt;
  &amp;lt;/url&amp;gt;
  {%- endfor %}
&amp;lt;/urlset&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Let's look at what is different
&lt;/h2&gt;

&lt;p&gt;We are now checking to see if the page data contains a &lt;code&gt;changefreq&lt;/code&gt; if it does, we use it, otherwise, we use &lt;em&gt;monthly&lt;/em&gt; as the default and we use the same approach with the page &lt;code&gt;priority&lt;/code&gt; - if the user has not set a page priority, we use a default of &lt;em&gt;0.5&lt;/em&gt; - that's it.&lt;/p&gt;

&lt;p&gt;A nice and easy fix to allow content authors more control over their pages whilst also having a default value automatically set if the author has not set one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;I love these quick wins - doing a small thing to make maintaining a website a lot easier. You can just set it and forget it.&lt;/p&gt;

</description>
      <category>eleventy</category>
      <category>ssg</category>
      <category>seo</category>
      <category>11ty</category>
    </item>
    <item>
      <title>Automated Open Graph images with 11ty and Cloudinary</title>
      <dc:creator>Juan Fernandes</dc:creator>
      <pubDate>Tue, 22 Sep 2020 23:00:00 +0000</pubDate>
      <link>https://dev.to/juanfernandes/automated-open-graph-images-with-11ty-and-cloudinary-4fg4</link>
      <guid>https://dev.to/juanfernandes/automated-open-graph-images-with-11ty-and-cloudinary-4fg4</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Cxhv9-pm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/r93g33918fnrnra4z5u7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Cxhv9-pm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/r93g33918fnrnra4z5u7.jpg" alt="Image showing post title: Automated Open Graph images with 11ty and Cloudinary"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I wanted to find a way to dynamically generate a unique Open Graph (OG) image for each blog post or note automatically using post data, the Cloudinary API and Eleventy (11ty) for social sharing.&lt;/p&gt;

&lt;p&gt;The purpose of this solution is to save time by not having to manually create a blog post image for each post and make blog posts stand out more when shared on social media websites like Twitter for example.&lt;/p&gt;

&lt;p&gt;Previously I was using a generic image for posts, or if I used a specific image for the blog post header, then that would become the OG image. But I wanted something unique for each post and something that would instantly tell people on social media what the post was about.&lt;/p&gt;

&lt;p&gt;There are many ways to achieve this, for example, &lt;a href="http://drewmclellan.net/"&gt;Drew McLellan&lt;/a&gt; wrote &lt;a href="https://24ways.org/2018/dynamic-social-sharing-images/"&gt;Dynamic Social Sharing Images&lt;/a&gt; back in 2018, but Drew is doing this at build time and is using some very clever stuff with Puppeteer and also this post, &lt;a href="https://dev.to/5t3ph/automated-social-sharing-images-with-puppeteer-11ty-and-netlify-22ln"&gt;Automated Social Sharing Images&lt;/a&gt; by &lt;a href="https://thinkdobecreate.com/"&gt;Stephanie Eckles&lt;/a&gt; that also uses Puppeteer.&lt;/p&gt;

&lt;p&gt;Those are great solutions, but I wanted something simpler to set up, lightweight on dependencies, not too technical and to just be able to &lt;em&gt;'set it and forget it'&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;What I wanted to achieve was to have an OpenGraph image that shows the post title on top of the image with my logo added to the image as well. This is what it will look like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bWWgvobL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juanfernandes/q_auto%2Cf_auto/opengraph-image_y6colg.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bWWgvobL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juanfernandes/q_auto%2Cf_auto/opengraph-image_y6colg.webp" alt="Image showing a blog post title and my logo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First you will need to make sure you have the OpenGraph tags for images added to the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of your page template, in my case I have this in my &lt;code&gt;blog.njk&lt;/code&gt; layout in eleventy. Here is the code I currently use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{% if postImage %}
&amp;lt;meta name="twitter:image" property="og:image" content="{{ site.cloudinary_url }}{{ postImage }}" /&amp;gt;
&amp;lt;meta name="twitter:image:alt" property="og:image:alt" content="{{ title }}" /&amp;gt;
{% else %}
&amp;lt;meta name="twitter:image" property="og:image" content="{{ site.meta.ogImg }}" /&amp;gt;
&amp;lt;meta name="twitter:image:alt" property="og:image:alt" content="{{ site.meta.ogImgAlt }}" /&amp;gt;
{% endif %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here I am checking to see if the post has specified a post image, if not, then use the default one (which I need to amend to use what I am writing about here). You'll notice that I have merged the OpenGraph tags (&lt;code&gt;name="twitter:image" property="og:image"&lt;/code&gt;) that Facebook and Twitter use instead of having multiple lines of code for the same thing. Note that you can specify ALT text for the image.&lt;/p&gt;

&lt;p&gt;Since I already use Cloudinary, I was happy to discover that their API has &lt;a href="https://cloudinary.com/documentation/image_transformations#image_and_text_overlays"&gt;text and image overlays&lt;/a&gt; that you can add to your images when you request them.&lt;/p&gt;

&lt;p&gt;This meant that my idea to have the post title and my logo overlaid on an image could be achieved.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does it work?
&lt;/h2&gt;

&lt;p&gt;This works by passing variables in the cloudinary image request URL, these variables are then used by the cloudinary API to create these custom images &lt;em&gt;on the fly&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Here is the complete URL for requesting an image with some custom overlays - in this case, the blog post title and my website logo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://res.cloudinary.com/juanfernandes/w_1200,f_auto/l_juanfernandes-logo,w_100,g_south_east,x_60,y_40/l_text:Georgia_80_bold_center:Using Defer to improve performance,co_rgb:eee,c_fit,w_600//v1579162296/computer-18363301920-1.jpg

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The great thing about this is that you can design your image right in the browser by adding a few variables to the URL.&lt;/p&gt;

&lt;p&gt;Now, this is how my OpenGraph images code in my &lt;code&gt;blog.njk&lt;/code&gt; layout template looks like using the Cloudinary variables and my blog post variables in Eleventy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;meta name="twitter:image" property="og:image" content="{{ site.cloudinary_url }}w_1200,f_auto/l_juanfernandes-logo,w_100,g_south_east,x_60,y_40/l_text:Georgia_80_bold_center:{{ title }},co_rgb:eee,c_fit,w_600/{{ postImage }}" /&amp;gt;&amp;lt;meta name="twitter:image:alt" property="og:image:alt" content="{{ title }}" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Let's break it down
&lt;/h2&gt;

&lt;p&gt;Essentially I am passing the post title text and image using Nunjucks variables, then I'm telling Cloudinary how to display that text and image overlay on top of the post image. Here is a breakdown of what all those variables mean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;https://res.cloudinary.com/juanfernandes/&lt;/code&gt; this is a variable I use to pass the cloudinary URL which my account name&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;w_1200,f_auto/&lt;/code&gt; here I am requesting the images at 1200px wide and automated image format&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;l_juanfernandes-logo&lt;/code&gt; this is the name of the image I want to use in the overlay, my logo, it's prepended with &lt;code&gt;l_&lt;/code&gt; and this is what tells cloudinary to use it as an overlay&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;w_100,g_south_east,x_60,y_40/&lt;/code&gt; this specifies the size of the image, 100px wide and the position, bottom right and its exact placement using X and Y values&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;l_text:Georgia_80_bold_center&lt;/code&gt; this tells cloudinary that the overlay text should be in Georgia font and the font size should be 80px, bold and centred&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Automated Open Graph images with 11ty and Cloudinary&lt;/code&gt; this is nunjucks variable for the blog post title&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;co_rgb:eee,c_fit,w_600&lt;/code&gt; this tells cloudinary the colour we want the overlay text to be, places it centred vertically and horizontally and sets a max-width for the text&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/v1579162296/computer-18363301920-1.jpg&lt;/code&gt; this variable passes the name of the post image already uploaded to cloudinary&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For general posts, like &lt;a href="https://dev.to/notes"&gt;Notes&lt;/a&gt;, I created a generic image using my brand colour and the text is added in the centre of the image and my logo at the bottom right.&lt;/p&gt;

&lt;p&gt;For posts where I have used a custom image, I make sure that it's a dark image to make the white text is legible.&lt;/p&gt;

&lt;p&gt;This was a quick solution and an easy win that will make my posts stand out a bit more on social media. I got to delve into and learn more about the Cloudinary API overlays in general.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tip: Testing your social sharing images
&lt;/h3&gt;

&lt;p&gt;Twitter and Facebook provide tools for you to test your social media 'cards' and here is what mine now looks like on Twitter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oJ0YaM-W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juanfernandes/q_auto%2Cf_auto/card_preview_twitter-dev.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oJ0YaM-W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juanfernandes/q_auto%2Cf_auto/card_preview_twitter-dev.png" alt="Image showing the twitter card preview tool"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cards-dev.twitter.com/validator"&gt;Twitter Card Validator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.facebook.com/tools/debug/"&gt;Facebook Sharing Debugger&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Taking this further
&lt;/h2&gt;

&lt;p&gt;I could take this a step further and pass the colour of the text or be able to choose a different logo or image to use as an overlay that can be set via a Content Management System, in my case that's &lt;a href="https://www.forestry.io"&gt;Forestry.io&lt;/a&gt; when creating a post.&lt;/p&gt;

&lt;p&gt;I could even turn this into an 11ty Plugin.&lt;/p&gt;

&lt;p&gt;Hope you find this useful and if you want to discuss anything about it, reach out to be on &lt;a href="https://twitter.com/juanfernandes"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>eleventy</category>
      <category>11ty</category>
      <category>ssg</category>
      <category>cloudinary</category>
    </item>
    <item>
      <title>Using Defer to improve performance</title>
      <dc:creator>Juan Fernandes</dc:creator>
      <pubDate>Mon, 03 Aug 2020 23:00:00 +0000</pubDate>
      <link>https://dev.to/juanfernandes/using-defer-to-improve-performance-46lj</link>
      <guid>https://dev.to/juanfernandes/using-defer-to-improve-performance-46lj</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zx0lNT9K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/culgj9klhk59xhy7hr49.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zx0lNT9K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/culgj9klhk59xhy7hr49.jpg" alt="Using defer to improve website performance"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In my last post, &lt;a href="https://www.juanfernandes.uk/blog/exploring-the-benefits-of-http-2/"&gt;Exploring the benefits of HTTP2&lt;/a&gt;, I wrote about removing jQuery and my &lt;code&gt;global.min.js&lt;/code&gt; files in favour of loading JavaScript on a per component or partial basis, so only when that component is included on a page it then loads the needed JavaScript code and library.&lt;/p&gt;

&lt;p&gt;Which lead me to look into Defer in JavaScript as another way to improve performance.&lt;/p&gt;

&lt;p&gt;"This Boolean attribute is set to indicate to a browser that the script is meant to be executed after the document has been parsed, but before firing DOMContentLoaded.&lt;/p&gt;

&lt;p&gt;Scripts with the defer attribute will prevent the DOMContentLoaded event from firing until the script has loaded and finished evaluating." - &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script"&gt;MDN Web Docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since the testimonials slider on my homepage is near the end of the page, I don't need to load the JavaScript right away and since it's not used on every page, it makes sense to only load it where the slider is being used.&lt;/p&gt;

&lt;p&gt;I read a few articles on Async and Defer to try and understand the difference between both and which was the correct one to use for the testimonials slider.&lt;/p&gt;

&lt;p&gt;So I figured I would just need to add the defer attribute to the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag for the TinySlider JavaScript and that would be it...&lt;/p&gt;

&lt;p&gt;But I found that doing this, it stopped the slider from working - I'm still learning JavaScript, so you will have to forgive me. I looked at the TinySlider example code I used and realised it was written so it would run as soon as the browser loads that part of the code.&lt;/p&gt;

&lt;p&gt;What I needed to do was change the code to only run when the DOM had finished loading and this meant that the HTML code for the slider would have been loaded too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;          &amp;lt;link rel="stylesheet" href="/assets/css/components/slider.css"&amp;gt;
          &amp;lt;script src='/assets/js/tiny-slider.js' defer&amp;gt;&amp;lt;/script&amp;gt;
          &amp;lt;script&amp;gt;
            document.addEventListener("DOMContentLoaded", () =&amp;gt; {
              let slider = tns({
                container: '.testimonials__slider',
                items: 1,
                autoHeight: true,
                speed: 400,
                loop: true,
                autoplay: true,
                autoplayButtonOutput: false,
                controls: false,
                autoplayHoverPause: true,
                nav: true,
                navPosition: "bottom",
              });
            });
          &amp;lt;/script&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






</description>
      <category>defer</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Exploring the benefits of HTTP/2</title>
      <dc:creator>Juan Fernandes</dc:creator>
      <pubDate>Sun, 03 May 2020 23:00:00 +0000</pubDate>
      <link>https://dev.to/juanfernandes/exploring-the-benefits-of-http-2-146d</link>
      <guid>https://dev.to/juanfernandes/exploring-the-benefits-of-http-2-146d</guid>
      <description>&lt;p&gt;Now that this website is hosted on &lt;a href="https://www.netlify.com"&gt;Netlify&lt;/a&gt; and they serve websites using the &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/HTTP_2"&gt;HTTP/2 protocol&lt;/a&gt; I've started exploring the benefits of it and what I need to change to take advantage of those benefits.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;HTTP/2 is a major revision of the HTTP network protocol. The primary goals for HTTP/2 are to reduce latency by enabling full request and response multiplexing, minimize protocol overhead via efficient compression of HTTP header fields, and add support for request prioritization and server push. - MDN Web Docs&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After some research, I found out that when a website is being served via HTTP/2, you no longer need to rely on the following techniques to improve the performance of your website:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Concatenated CSS and JS&lt;/li&gt;
&lt;li&gt;Inline CSS (criticalCSS)&lt;/li&gt;
&lt;li&gt;Use of a CDN (Content Distribution Network)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This website is fairly simple and does not rely on a lot of JavaScript but does use it for a couple of sliders, one on the homepage for testimonials and another on each of the work pages.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Old Way
&lt;/h2&gt;

&lt;p&gt;What I was doing before was ti load jQuery in the footer on all pages along with a &lt;code&gt;global.min.js&lt;/code&gt; file - which was a concatenated file made up of my &lt;code&gt;main.js&lt;/code&gt; and &lt;code&gt;plugins.js&lt;/code&gt; files.&lt;/p&gt;

&lt;p&gt;The file was minified, but it's still a lot of JS to be loaded on every single page, even if it wasn't being used. And to top it all off, I was also loading jQuery.&lt;/p&gt;

&lt;h2&gt;
  
  
  Out with the old, in with the new... way
&lt;/h2&gt;

&lt;p&gt;The first thing I decided to do was to get rid of jQuery - there's nothing wring with jQuery, I know its no longer flavour of the month, but it still has its place and it's still used by a lot of websites, but for my tiny website, it was overkill.&lt;/p&gt;

&lt;p&gt;So I replaced the jQuery slider with a vanilla JS alternative, &lt;a href="https://github.com/ganlanyuan/tiny-slider"&gt;TinySlider&lt;/a&gt;. I spent some time researching to find a slider that was small in size as well as accessible. I'm not an accessibility expert, so I just made sure that the slider was navigable using a keyboard.&lt;/p&gt;

&lt;p&gt;This meant I could remove the code in the footer that loaded the jQuery library from the jQuery CDN and also stopped including the &lt;code&gt;plugins.js&lt;/code&gt; file into the &lt;code&gt;global.min.js&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;It's a great start, the global.min.js file was now smaller and I was no longer loading jQuery.&lt;/p&gt;

&lt;p&gt;But I didn't stop there. One of the advantages of HTTP/2 is that you don't have to worry about too many file requests.&lt;/p&gt;

&lt;p&gt;Instead of including the JavaScript code for the slider on every single page. I decided to only include the slider code in the slider partial - I'm using Nunjucks for this, but you can do this with other templating languages.&lt;/p&gt;

&lt;p&gt;This is the code for the testimonials slider on my homepage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;          &amp;lt;link rel="stylesheet" href="/assets/css/components/slider.css"&amp;gt;
          &amp;lt;script src='/assets/js/tiny-slider.js' defer&amp;gt;&amp;lt;/script&amp;gt;
          &amp;lt;script&amp;gt;
            document.addEventListener("DOMContentLoaded", () =&amp;gt; {
              let slider = tns({
                container: '.testimonials__slider',
                items: 1,
                autoHeight: true,
                speed: 400,
                loop: true,
                autoplay: true,
                autoplayButtonOutput: false,
                controls: false,
                autoplayHoverPause: true,
                nav: true,
                navPosition: "bottom",
              });
            });
          &amp;lt;/script&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the same time, I also included the CSS or the slider - just on the slider partial.&lt;/p&gt;

&lt;p&gt;The JS and CSS files, as well as the slider JS code, are included the at the end of the slider partial so that when the JS runs, the HTML for the slider has already been loaded by the DOM.&lt;/p&gt;

&lt;p&gt;This change has improved my websites' overall performance score on Lighthouse - but there is one more thing I need to fix before its perfect.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is all new to me and I may have made a mistake or misunderstood something, if you spot something, please let me know -&lt;/em&gt; &lt;a href="https://twitter.com/juanfernandes"&gt;&lt;em&gt;send me a tweet&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;May the 4th be with you&lt;/strong&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Multiple blogs with Perch CMS</title>
      <dc:creator>Juan Fernandes</dc:creator>
      <pubDate>Wed, 13 Mar 2019 13:00:08 +0000</pubDate>
      <link>https://dev.to/juanfernandes/multiple-blogs-with-perch-cms-2f2g</link>
      <guid>https://dev.to/juanfernandes/multiple-blogs-with-perch-cms-2f2g</guid>
      <description>&lt;p&gt;An existing client asked me to move their blog from /self-help/blog/ to the root of the website - but they wanted to keep the self-help blog, as well as having a blog for general posts and a blog for media related articles, ie press releases etc.&lt;/p&gt;

&lt;p&gt;I wondered if the perch blog app had a built-in way of doing this - it does but not in the way I thought it would. It does have &lt;code&gt;perch_blog_section()&lt;/code&gt; which allows you to output a blog section onto a page.&lt;/p&gt;

&lt;p&gt;You can think of blog sections as virtual folders where you can store some blog posts. So you still write all your blog posts in the same way as before, but you choose which section it belongs to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the sections
&lt;/h2&gt;

&lt;p&gt;To get started you need to log into perch and create your new sections. Once you're logged in:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on Blog -&amp;gt; Sections&lt;/li&gt;
&lt;li&gt;Click the &lt;em&gt;Add section&lt;/em&gt; button&lt;/li&gt;
&lt;li&gt;Add the &lt;em&gt;Title&lt;/em&gt; [image is not required]&lt;/li&gt;
&lt;li&gt;Click &lt;em&gt;Save&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For my client, I created the sections based on where they were going to be shown &lt;em&gt;media&lt;/em&gt; and &lt;em&gt;self-help&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;You can leave &lt;em&gt;Posts&lt;/em&gt; as the blog's default section.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multiple blogs
&lt;/h2&gt;

&lt;p&gt;We can now create our multiple blogs. Depending on the website you're working on, you'll either need to create a new page or edit an existing one.&lt;/p&gt;

&lt;p&gt;For my clients' website, I created a new page using the default template and then added the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
  &lt;span class="nx"&gt;perch_blog_custom&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'section'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'media'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'sort'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'postDateTime'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'sort-order'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'DESC'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'template'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'blog/media_post_in_list.html'&lt;/span&gt;
  &lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you already use Perch, then the above code will look fairly familiar, but if you don't.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Line 3&lt;/em&gt; - instead of getting all the blog posts, we are calling just blog posts that belong to the &lt;em&gt;media&lt;/em&gt; section&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Lines 4-5&lt;/em&gt; - we are sorting the blog posts in the date they were created and showing them in descending order&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Line 6&lt;/em&gt; - this specifies the template to use. This one is specific to my clients' website. You can just use your own one or the default &lt;code&gt;post_in_list.html&lt;/code&gt; template.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using the above code, you can create your multiple blogs in different areas of your website.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the content
&lt;/h2&gt;

&lt;p&gt;Now that you have the code to show posts from different sections on different pages of your website, you can create blogs posts and add them to each section.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Login to Perch&lt;/li&gt;
&lt;li&gt;Go to the Blog app&lt;/li&gt;
&lt;li&gt;Create or edit a post&lt;/li&gt;
&lt;li&gt;Switch to the &lt;em&gt;Meta and Social&lt;/em&gt; tab&lt;/li&gt;
&lt;li&gt;Scroll down and select the section from the dropdown&lt;/li&gt;
&lt;li&gt;Save&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, go to the page for that blog section on your website and see your posts for that blog section.&lt;/p&gt;

&lt;h3&gt;
  
  
  If you're also moving a blog
&lt;/h3&gt;

&lt;p&gt;In my case, I first moved the blog folder to the root of the website, changed all the URLs, updated the settings for the blog app in Perch settings and changed rewrites in the &lt;code&gt;.htaccess&lt;/code&gt; file.&lt;/p&gt;

</description>
      <category>perch</category>
      <category>cms</category>
      <category>frontend</category>
    </item>
    <item>
      <title>My Git Aliases</title>
      <dc:creator>Juan Fernandes</dc:creator>
      <pubDate>Thu, 31 Jan 2019 16:05:36 +0000</pubDate>
      <link>https://dev.to/juanfernandes/my-git-aliases-5569</link>
      <guid>https://dev.to/juanfernandes/my-git-aliases-5569</guid>
      <description>&lt;p&gt;I use Git via the command line and have always used it that way ever since it was introduced at a company I worked at a few years ago.&lt;/p&gt;

&lt;p&gt;But soon after using it Git for a few days, I started disliking the repetitiveness of the commands - so I did some googling and found that I could create shortcuts (aliases) in Bash.&lt;/p&gt;

&lt;p&gt;So I started creating aliases for the commands I used several times a day and over the years I have added to them as I find other developers' own git aliases.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to create an alias
&lt;/h2&gt;

&lt;p&gt;You can create an alias in a .bash_profile or in . bashrc, usually in the your users home directory.&lt;/p&gt;

&lt;p&gt;A bash alias takes on this format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alias ALIAS_NAME="ALIAS_COMMAND"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here are my aliases:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.juanfernandes.uk%2Fassets%2Fimgs%2Faliases.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.juanfernandes.uk%2Fassets%2Fimgs%2Faliases.png" title="List of GIT aliases" alt="List of GIT aliases"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see the list on this &lt;a href="https://gist.github.com/juanfernandes/7e13fa0c81253ae46f8d" rel="noopener noreferrer"&gt;Github gist&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bash the fish
&lt;/h2&gt;

&lt;p&gt;With my new laptop (MacBook Pro) I decided to try Fish, a bash alternative, so creating aliases with fish is not done the same way - they are know as functions in Fish.&lt;/p&gt;

&lt;p&gt;Instead of adding them to a file, you create them on the command line, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alias x='exit'

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, save it using this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;funcsave x

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The functions are then saved in a folder: ~/.config/fish/functions/ - to see all the functions in a web based interface, type the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fish_config functions

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I won't get too deep into Fish, as I'm still learning it - maybe a future post.&lt;/p&gt;

&lt;p&gt;Did you find this useful? Have you got a your own set of aliases - please share them.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
