<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Luke Morrigan</title>
	<subtitle></subtitle>
	<link href="https://codefoodpixels.com/rss.xml" rel="self"/>
	<link href="https://codefoodpixels.com/"/>
	
	<updated>2022-12-07T10:30:00Z</updated>
	
	<id>https://codefoodpixels.com</id>
	<author>
    <name>Luke Morrigan</name>
    <email>luke@codefoodpixels.com</email>
	</author>
	
  
  <entry>
    <title>Letting Eleventy Schedule Its Own Builds</title>
    <link href="https://codefoodpixels.com/blog/2022/12/07/letting-eleventy-schedule-its-own-builds/"/>
    <updated>2022-12-07T10:30:00Z</updated>
    <id>https://codefoodpixels.com/blog/2022/12/07/letting-eleventy-schedule-its-own-builds/</id>
    <content type="html"><![CDATA[
      <p>Until recently, I've been using GitHub Actions to trigger builds on my website, but this approach meant that builds for the site were only run once a day and that I couldn't schedule specific times for posts to go live. In February of this year, <a href="https://www.netlify.com/blog/quirrel-joins-netlify-and-scheduled-functions-launches-in-beta/" target="_blank" rel="external noopener noreferrer">Netlify announced Scheduled Functions</a>, and one of the use cases that I'd seen mentioned was scheduling builds.</p>
<h2 id="netlify-scheduled-functions">Netlify Scheduled Functions</h2>
<p>Netlify Functions are serverless functions that can be versioned, built, and deployed along with the rest of your site. Scheduled Functions take this a step further and allow you to run the functions at certain times using the <a href="https://en.wikipedia.org/wiki/Cron" target="_blank" rel="external noopener noreferrer">cron</a> format.</p>
<h2 id="building-a-scheduled-function">Building a Scheduled Function</h2>
<p>There are a couple of ways to define a Scheduled Function, but we're going to focus on defining it all in the function code. You can see more about this in the <a href="https://docs.netlify.com/functions/scheduled-functions/#writing-a-scheduled-function" target="_blank" rel="external noopener noreferrer">Netlify Scheduled Functions documentation</a>.</p>
<p>To define it in the code, we'll need the <code>@netlify/functions</code> package, so we need to install it:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">npm</span> <span class="token function">install</span> @netlify/functions</code></pre>
<p>We'll be using the <code>schedule</code> method from this package, and this method takes 2 parameters:</p>
<ol>
<li><code>cron expression</code>, this is a cron pattern that defines when the Scheduled Function runs. <a href="https://crontab.guru/" target="_blank" rel="external noopener noreferrer">crontab guru</a> can help you build this expression if you want a specific pattern.</li>
<li><code>callback function</code>, this is the function that will be called.</li>
</ol>
<p>So let's create a basic Scheduled Function that prints &quot;Hello world!&quot; to the logs:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">const</span> <span class="token punctuation">{</span> schedule <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"@netlify/functions"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />exports<span class="token punctuation">.</span>handler <span class="token operator">=</span> <span class="token function">schedule</span><span class="token punctuation">(</span><span class="token string">"* * * * *"</span><span class="token punctuation">,</span> <span class="token keyword">await</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br />  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"Hello world!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />  <span class="token keyword">return</span> <span class="token punctuation">{</span><br />    <span class="token literal-property property">statusCode</span><span class="token operator">:</span> <span class="token number">200</span><br />  <span class="token punctuation">}</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
<p>In the above example, we <code>require</code> the <code>@netlify/functions</code> package and use the destructuring assignment to unpack <code>schedule</code> from it. We then call <code>schedule</code> with a cron value and a callback, and then assign that to <code>exports.handler</code>, which is what Netlify Functions will run.</p>
<p>In the <code>schedule</code> call, we use <code>* * * * *</code> as the cron value, which means that it will run every minute. As the callback value, we have a function that calls <code>console.log</code> to write &quot;Hello world!&quot; to the console and then returns a response object that contains the key <code>statusCode</code> with the value <code>200</code>.</p>
<p>We can then save this file within our Netlify site directory as <code>netlify/functions/hello.js</code> and when Netlify deploys our site, we'll see &quot;Hello world!&quot; being printed to the logs in the Netlify UI!</p>
<h2 id="rebuilding-your-netlify-site-with-scheduled-functions">Rebuilding your Netlify site with Scheduled Functions</h2>
<p>So the example above isn't particularly useful, but it gives us a base for building something that can trigger a rebuild of our Netlify site. We can do this with <a href="https://docs.netlify.com/configure-builds/build-hooks/" target="_blank" rel="external noopener noreferrer">Netlify's build hooks</a>, which is a URL that we can do a <code>POST</code> request to and tell Netlify to start a build.</p>
<p>I'd recommend that you store the build hook (either entirely or the identifier from the end) in your <a href="https://docs.netlify.com/environment-variables/overview/" target="_blank" rel="external noopener noreferrer">Netlify environment variables</a>.</p>
<p>To make the <code>POST</code> request, we can use Node's built in <code>https</code> module.</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">const</span> <span class="token punctuation">{</span> request <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"https"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token keyword">const</span> <span class="token punctuation">{</span> schedule <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"@netlify/functions"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />exports<span class="token punctuation">.</span>handler <span class="token operator">=</span> <span class="token function">schedule</span><span class="token punctuation">(</span><span class="token string">"30 10 * * *"</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br />  <span class="token keyword">await</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">resolve<span class="token punctuation">,</span> reject</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br />    <span class="token keyword">const</span> req <span class="token operator">=</span> <span class="token function">request</span><span class="token punctuation">(</span><br />      <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">https://api.netlify.com/build_hooks/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">BUILD_HOOK</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span><br />      <span class="token punctuation">{</span> <span class="token literal-property property">method</span><span class="token operator">:</span> <span class="token string">"POST"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br />      <span class="token punctuation">(</span><span class="token parameter">res</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br />        console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"statusCode:"</span><span class="token punctuation">,</span> res<span class="token punctuation">.</span>statusCode<span class="token punctuation">)</span><span class="token punctuation">;</span><br />        <span class="token function">resolve</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br />      <span class="token punctuation">}</span><br />    <span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />    req<span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">"error"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br />      console<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">;</span><br />      <span class="token function">reject</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br />    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br />    req<span class="token punctuation">.</span><span class="token function">end</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br />  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />  <span class="token keyword">return</span> <span class="token punctuation">{</span><br />    <span class="token literal-property property">statusCode</span><span class="token operator">:</span> <span class="token number">200</span><span class="token punctuation">,</span><br />  <span class="token punctuation">}</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>In this example we're still using the <code>schedule</code> method, but the schedule is now <code>30 10 * * *</code> which runs it every day at 10:30, and the callback function uses <code>https.request</code> to send a <code>POST</code> request to our build hook.</p>
<p>So now we have something that rebuilds the site once a day, but we already had that with GitHub Actions. Lets make it more specific!</p>
<h2 id="using-eleventy-to-generate-netlify-scheduled-functions">Using Eleventy to generate Netlify Scheduled Functions</h2>
<p>Netlify doesn't deploy any functions until after the build process for your site has completed, and this means that we can generate or modify our Netlify Functions at build time!</p>
<p>First up, we need to separate live posts and future posts so that only the posts that should be live are listed on any pages. To do this, we'll create 2 new collections in our <code>.eleventy.js</code>: <code>posts</code> and <code>futurePosts</code></p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">const</span> now <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />eleventyConfig<span class="token punctuation">.</span><span class="token function">addCollection</span><span class="token punctuation">(</span><span class="token string">"posts"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">collectionApi</span><span class="token punctuation">)</span> <span class="token operator">=></span><br />  collectionApi<br />    <span class="token punctuation">.</span><span class="token function">getFilteredByGlob</span><span class="token punctuation">(</span><span class="token string">"./src/posts/*"</span><span class="token punctuation">)</span><br />    <span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">post</span><span class="token punctuation">)</span> <span class="token operator">=></span> post<span class="token punctuation">.</span>date <span class="token operator">&lt;=</span> now<span class="token punctuation">)</span><br />    <span class="token punctuation">.</span><span class="token function">reverse</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />eleventyConfig<span class="token punctuation">.</span><span class="token function">addCollection</span><span class="token punctuation">(</span><span class="token string">"futurePosts"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">collectionApi</span><span class="token punctuation">)</span> <span class="token operator">=></span><br />  collectionApi<br />    <span class="token punctuation">.</span><span class="token function">getFilteredByGlob</span><span class="token punctuation">(</span><span class="token string">"./src/posts/*"</span><span class="token punctuation">)</span><br />    <span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">post</span><span class="token punctuation">)</span> <span class="token operator">=></span> post<span class="token punctuation">.</span>date <span class="token operator">></span> now<span class="token punctuation">)</span><br /><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Now that we have our collections, it's important to update any pages that list posts to refer to our new <code>posts</code> collection so that we only show the posts that are aready live.</p>
<p>Next we need to create the file that will generate our Scheduled Function. I've called mine <code>buildFunction.11ty.js</code> and it's using the <code>11ty.js</code> template format.</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">class</span> <span class="token class-name">BuildFunction</span> <span class="token punctuation">{</span><br />  <span class="token function">data</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br />    <span class="token keyword">return</span> <span class="token punctuation">{</span><br />      <span class="token literal-property property">permalink</span><span class="token operator">:</span> <span class="token string">"netlify/functions/build.js"</span><span class="token punctuation">,</span><br />      <span class="token literal-property property">permalinkBypassOutputDir</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span><br />    <span class="token punctuation">}</span><span class="token punctuation">;</span><br />  <span class="token punctuation">}</span><br /><br />  <span class="token function">dateToCron</span><span class="token punctuation">(</span><span class="token parameter">date</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br />    <span class="token keyword">return</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>date<span class="token punctuation">.</span><span class="token function">getMinutes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>date<span class="token punctuation">.</span><span class="token function">getHours</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>date<span class="token punctuation">.</span><span class="token function">getDate</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><br />      date<span class="token punctuation">.</span><span class="token function">getMonth</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">1</span><br />    <span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>date<span class="token punctuation">.</span><span class="token function">getDay</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span><br />  <span class="token punctuation">}</span><br /><br />  <span class="token function">render</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> collections<span class="token punctuation">,</span> site <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br />    <span class="token keyword">const</span> nextYear <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br />    nextYear<span class="token punctuation">.</span><span class="token function">setFullYear</span><span class="token punctuation">(</span>nextYear<span class="token punctuation">.</span><span class="token function">getFullYear</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br />    nextYear<span class="token punctuation">.</span><span class="token function">setHours</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br />    nextYear<span class="token punctuation">.</span><span class="token function">setMinutes</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br />    nextYear<span class="token punctuation">.</span><span class="token function">setSeconds</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />    <span class="token keyword">const</span> postDates <span class="token operator">=</span> collections<span class="token punctuation">.</span>futurePosts<br />      <span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">post</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br />        <span class="token keyword">return</span> post<span class="token punctuation">.</span>date<span class="token punctuation">;</span><br />      <span class="token punctuation">}</span><span class="token punctuation">)</span><br />      <span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">date</span><span class="token punctuation">)</span> <span class="token operator">=></span> date <span class="token operator">&lt;=</span> nextYear<span class="token punctuation">)</span><br />      <span class="token punctuation">.</span><span class="token function">sort</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token operator">=></span> a <span class="token operator">-</span> b<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />    postDates<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>nextYear<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />    <span class="token keyword">return</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string"><br />const { request } = require("https");<br />const { schedule } = require("@netlify/functions");<br /><br />exports.handler = schedule("</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">dateToCron</span><span class="token punctuation">(</span>postDates<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">", async () => {<br />  await new Promise((resolve, reject) => {<br />    const req = request(<br />      "https://api.netlify.com/build_hooks/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">BUILD_HOOK</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">",<br />      { method: "POST" },<br />      (res) => {<br />        console.log("statusCode:", res.statusCode);<br />        resolve();<br />      }<br />    );<br /><br />    req.on("error", (e) => {<br />      console.error(e);<br />      reject();<br />    });<br />    req.end();<br />  });<br /><br />  return {<br />    statusCode: 200,<br />  };<br />});</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span><br />  <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><br /><br />module<span class="token punctuation">.</span>exports <span class="token operator">=</span> BuildFunction<span class="token punctuation">;</span></code></pre>
<p>Above is the complete code for this file, but I'll go through the various parts of it</p>
<h3 id="data-method"><code>data</code> method</h3>
<p>The data method allows us to specify the frontmatter data for this file. Here we've set the <code>permalink</code> attribute to <code>&quot;netlify/functions/build.js&quot;</code> and the <code>permalinkBypassOutputDir</code> attribute to <code>true</code>. This means that Eleventy will build this file to <code>netlify/functions/build.js</code>, starting from your project root directory.</p>
<h3 id="render-method"><code>render</code> method</h3>
<p>In our render method, we first <code>map</code> over our <code>futurePosts</code> collection so that we have an array of dates that posts will be live. Then, as cron doesn't specify a year, we filter the array to only have dates within the next year. Next, we sort the array so that we have the nearest date first. Just in case we don't have any posts due to go live in the next year, we push the date for 1 year from now into the array too.</p>
<p>Finally, we return our function code using a template string. We insert our cron pattern using a <code>dateToCron</code> method which takes a date and converts it into a cron pattern, and we insert our <code>BUILD_HOOK</code> environment variable.</p>
<h2 id="timezones">Timezones</h2>
<p>An important thing to note is that Netlify uses UTC for times, so if you have <code>25th December 2022 10:30</code> in your post and you're expecting it to post at 10:30 in your local timezone, you'll need to convert the dates from your timezone to UTC.</p>
<p>I do this using the <code>zonedTimeToUtc</code> method from the <code>date-fns-tz</code> package, with a method like this:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token function">getUTCPostDate</span><span class="token punctuation">(</span><span class="token parameter">date</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br />  <span class="token keyword">const</span> <span class="token function-variable function">padded</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">val</span><span class="token punctuation">)</span> <span class="token operator">=></span> val<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">padStart</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token string">"0"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />  <span class="token keyword">return</span> <span class="token function">zonedTimeToUtc</span><span class="token punctuation">(</span><br />    <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>date<span class="token punctuation">.</span><span class="token function">getFullYear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">-</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token function">padded</span><span class="token punctuation">(</span>date<span class="token punctuation">.</span><span class="token function">getMonth</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">-</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token function">padded</span><span class="token punctuation">(</span><br />      date<span class="token punctuation">.</span><span class="token function">getDate</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br />    <span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token function">padded</span><span class="token punctuation">(</span>date<span class="token punctuation">.</span><span class="token function">getHours</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">:</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token function">padded</span><span class="token punctuation">(</span>date<span class="token punctuation">.</span><span class="token function">getMinutes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">:</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token function">padded</span><span class="token punctuation">(</span><br />      date<span class="token punctuation">.</span><span class="token function">getSeconds</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br />    <span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span><br />    <span class="token string">"Europe/London"</span><br />  <span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
<h2 id="wrapping-up">Wrapping up</h2>
<p>This post was published with this method! I figured what better post to test it on than a post about the thing itself. Is that <a href="https://en.wikipedia.org/wiki/Eating_your_own_dog_food" target="_blank" rel="external noopener noreferrer">dogfooding</a>?</p>
<p>Anyway, I hope this helps you figure out how to use Netlify Scheduled Functions to rebuild your own site!</p>
<h2 id="edit-2022-12-07-1412">Edit (2022-12-07 14:12)</h2>
<p>I noticed that my Scheduled Function ran 3 times, and this was down to Netlify requiring an async function to be passed. I've updated the examples above.</p>

    ]]></content>
  </entry>
	
  
  <entry>
    <title>No Comment 2: The Webmentioning</title>
    <link href="https://codefoodpixels.com/blog/2022/06/28/no-comment-2-the-webmentioning/"/>
    <updated>2022-06-28T10:30:00Z</updated>
    <id>https://codefoodpixels.com/blog/2022/06/28/no-comment-2-the-webmentioning/</id>
    <content type="html"><![CDATA[
      <p>When version 1.0.0 of Eleventy was released, one of the features that I was excited to see was the ability to add global data through configuration. This means that plugins can now add global data, and I saw it as a great opportunity to move <a href="https://codefoodpixels.com/blog/2021/03/15/no-comment-adding-webmentions-to-my-site/">my webmentions code</a> into a plugin for others to use!</p>
<p>When I started to look into how to create a plugin, I quickly realised that in the Eleventy world, a plugin is just an extra <code>.eleventy.js</code> file that gets loaded 🤯. What an amazingly simple way to create plugins!</p>
<p>Because it's just an extra <code>.eleventy.js</code>, you have access to everything you can do in an Eleventy config file. In my case, I needed <code>.addFilter</code> to add the <code>webmentionsForPage</code> and <code>webmentionCountForPage</code> filters, and <code>.addGlobalData</code> to add the webmentions to the global data.</p>
<p>You can <a href="https://www.npmjs.com/package/eleventy-plugin-webmentions" target="_blank" rel="external noopener noreferrer">install it from npm</a> and the load it using <code>.addPlugin</code>, like this:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">const</span> Webmentions <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"eleventy-plugin-webmentions"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">eleventyConfig</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br />  eleventyConfig<span class="token punctuation">.</span><span class="token function">addPlugin</span><span class="token punctuation">(</span>Webmentions<span class="token punctuation">,</span> <span class="token punctuation">{</span><br />    <span class="token literal-property property">domain</span><span class="token operator">:</span> <span class="token string">"codefoodpixels.com"</span><span class="token punctuation">,</span><br />    <span class="token literal-property property">token</span><span class="token operator">:</span> <span class="token string">"ABC123XYZ987"</span><span class="token punctuation">,</span><br />  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>This adds a <code>webmentions</code> global data object, and then in your templates, you can use the <code>webmentionsForPage</code> and <code>webmentionCountForPage</code> to filter <code>webmentions</code>.</p>
<p>Full documentation, including a load of configurable options is in the <a href="https://github.com/CodeFoodPixels/eleventy-plugin-webmentions#readme" target="_blank" rel="external noopener noreferrer">readme on GitHub</a>.</p>

    ]]></content>
  </entry>
	
  
  <entry>
    <title>Pixelated rounded corners with CSS clip-path</title>
    <link href="https://codefoodpixels.com/blog/2022/01/17/pixelated-rounded-corners-with-css-clip-path/"/>
    <updated>2022-01-17T10:30:00Z</updated>
    <id>https://codefoodpixels.com/blog/2022/01/17/pixelated-rounded-corners-with-css-clip-path/</id>
    <content type="html"><![CDATA[
      <p>I remember the day in 2011 when the design agency that I worked for decided that we were dropping IE6 support and raising our minimum to IE 9. Among other things, we didn't have to use images for rounded corners any more, we could use <code>border-radius</code>!</p>
<p>Adding rounded corners to items such as images and buttons makes them feel a bit softer and more aesthetically pleasing, but I didn't feel that regular rounded corners would suit the style of my website. That's when I had the idea to use <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/clip-path" target="_blank" rel="external noopener noreferrer">CSS <code>clip-path</code></a>.</p>
<p>CSS <code>clip-path</code> allows us to determine what part of an element should be shown. We can define a path and anything outside that path will be hidden. There are a number of different shape functions we can use, these are:</p>
<ul>
<li><code>circle</code></li>
<li><code>ellipse</code></li>
<li><code>inset</code> - defines an inset rectangle</li>
<li><code>polygon</code> - a set of x and y coordinates for a path</li>
<li><code>path</code> - an SVG path string</li>
</ul>
<p>For this article, we'll be focusing on <code>polygon</code>.</p>
<p>The <code>polygon</code> shape function takes a set of x and y coordinates to make up a path, the x and y values being an offset from the top left of the element. Each of these sets of coordinates can be defined as any <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/length" target="_blank" rel="external noopener noreferrer">valid CSS length</a> or as a percentage.</p>
<p>In all of the examples on this page, I'll be using images as they help visualise the things I'm talking about, but <code>clip-path</code> can be applied to any HTML element.</p>
<p>The example below shows how we can apply <code>clip-path</code> to an image using pixel values to show only a specified area.</p>
<div class="eleventy-plugin-embedded-demo__container">
  <details class="eleventy-plugin-embedded-demo__code" false="">
  <summary class="eleventy-plugin-embedded-demo__code-toggle">HTML</summary>
  <pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span><br />  <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/static/images/t5vV7unhLw-300.jpeg<span class="token punctuation">"</span></span><br />  <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>300<span class="token punctuation">"</span></span><br />  <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>200<span class="token punctuation">"</span></span><br />  <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>A clear gameboy and 3 games clipped into a triangle<span class="token punctuation">"</span></span><br />  <span class="token attr-name">loading</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lazy<span class="token punctuation">"</span></span><br />  <span class="token attr-name">decoding</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>async<span class="token punctuation">"</span></span><br />  <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>triangle-1<span class="token punctuation">"</span></span><br /><span class="token punctuation">/></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span><br />  <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/static/images/t5vV7unhLw-300.jpeg<span class="token punctuation">"</span></span><br />  <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>300<span class="token punctuation">"</span></span><br />  <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>200<span class="token punctuation">"</span></span><br />  <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>A clear gameboy and 3 games clipped into a smaller triangle<span class="token punctuation">"</span></span><br />  <span class="token attr-name">loading</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lazy<span class="token punctuation">"</span></span><br />  <span class="token attr-name">decoding</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>async<span class="token punctuation">"</span></span><br />  <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>triangle-2<span class="token punctuation">"</span></span><br /><span class="token punctuation">/></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span><br />  Photo by<br />  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span><br />    <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://unsplash.com/@dimhou<span class="token punctuation">"</span></span><br />    <span class="token attr-name">target</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>_blank<span class="token punctuation">"</span></span><br />    <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>external noopener noreferrer<span class="token punctuation">"</span></span><br />    <span class="token punctuation">></span></span>Dim Hou<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><br />  <span class="token punctuation">></span></span><br />  on Unsplash<br /><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span></code></pre>
</details><details class="eleventy-plugin-embedded-demo__code" false="">
  <summary class="eleventy-plugin-embedded-demo__code-toggle">CSS</summary>
  <pre class="language-css"><code class="language-css"><span class="token selector">.triangle-1</span> <span class="token punctuation">{</span><br />  <span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>150px 0px<span class="token punctuation">,</span> 300px 200px<span class="token punctuation">,</span> 0px 200px<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><br /><span class="token selector">.triangle-2</span> <span class="token punctuation">{</span><br />  <span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>75px 0px<span class="token punctuation">,</span> 150px 100px<span class="token punctuation">,</span> 0px 100px<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
</details>
  <details class="eleventy-plugin-embedded-demo__result" open="">
    <summary class="eleventy-plugin-embedded-demo__result-toggle">Result</summary>
    <iframe src="https://codefoodpixels.com/demo/clip-path/basic/" title="Basic clip-path demo" loading="lazy" class="eleventy-plugin-embedded-demo__result-iframe"></iframe>
  </details>
</div>
<p>This is fine if we know the size of our images and want to apply it to images with that particular size, otherwise we'll end up cropping out bits of larger or differently proportioned images. In the below example, the same image and <code>clip-path</code> are used, but one image is <code>300px</code> and the other is <code>600px</code>. As we can see, it shows the same size section of the image, but it shows different parts of the image.</p>
<div class="eleventy-plugin-embedded-demo__container">
  <details class="eleventy-plugin-embedded-demo__code" false="">
  <summary class="eleventy-plugin-embedded-demo__code-toggle">HTML</summary>
  <pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span><br />  <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/static/images/t5vV7unhLw-300.jpeg<span class="token punctuation">"</span></span><br />  <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>300<span class="token punctuation">"</span></span><br />  <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>200<span class="token punctuation">"</span></span><br />  <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>A clear gameboy and 3 games clipped into a triangle<span class="token punctuation">"</span></span><br />  <span class="token attr-name">loading</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lazy<span class="token punctuation">"</span></span><br />  <span class="token attr-name">decoding</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>async<span class="token punctuation">"</span></span><br />  <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>triangle<span class="token punctuation">"</span></span><br /><span class="token punctuation">/></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span><br />  <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/static/images/t5vV7unhLw-600.jpeg<span class="token punctuation">"</span></span><br />  <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>600<span class="token punctuation">"</span></span><br />  <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>400<span class="token punctuation">"</span></span><br />  <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>A clear gameboy and 3 games clipped into a triangle<span class="token punctuation">"</span></span><br />  <span class="token attr-name">loading</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lazy<span class="token punctuation">"</span></span><br />  <span class="token attr-name">decoding</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>async<span class="token punctuation">"</span></span><br />  <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>triangle<span class="token punctuation">"</span></span><br /><span class="token punctuation">/></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span><br />  Photo by<br />  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span><br />    <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://unsplash.com/@dimhou<span class="token punctuation">"</span></span><br />    <span class="token attr-name">target</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>_blank<span class="token punctuation">"</span></span><br />    <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>external noopener noreferrer<span class="token punctuation">"</span></span><br />    <span class="token punctuation">></span></span>Dim Hou<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><br />  <span class="token punctuation">></span></span><br />  on Unsplash<br /><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span></code></pre>
</details><details class="eleventy-plugin-embedded-demo__code" false="">
  <summary class="eleventy-plugin-embedded-demo__code-toggle">CSS</summary>
  <pre class="language-css"><code class="language-css"><span class="token selector">.triangle</span> <span class="token punctuation">{</span><br />  <span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>150px 0px<span class="token punctuation">,</span> 300px 200px<span class="token punctuation">,</span> 0px 200px<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
</details>
  <details class="eleventy-plugin-embedded-demo__result" open="">
    <summary class="eleventy-plugin-embedded-demo__result-toggle">Result</summary>
    <iframe src="https://codefoodpixels.com/demo/clip-path/basic-wrong-size/" title="Basic clip-path demo with different sized images" loading="lazy" class="eleventy-plugin-embedded-demo__result-iframe"></iframe>
  </details>
</div>
<p>If we want to allow the <code>clip-path</code> to flex and fit the image that we're applying it to, we can use percentages. This means that the clip path is based on a percentage of the image's dimensions, as can be seen below.</p>
<div class="eleventy-plugin-embedded-demo__container">
  <details class="eleventy-plugin-embedded-demo__code" false="">
  <summary class="eleventy-plugin-embedded-demo__code-toggle">HTML</summary>
  <pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span><br />  <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/static/images/jPB8yppx1f-300.jpeg<span class="token punctuation">"</span></span><br />  <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>300<span class="token punctuation">"</span></span><br />  <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>200<span class="token punctuation">"</span></span><br />  <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>A green gameboy color held in a hand and clipped into a triangle<span class="token punctuation">"</span></span><br />  <span class="token attr-name">loading</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lazy<span class="token punctuation">"</span></span><br />  <span class="token attr-name">decoding</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>async<span class="token punctuation">"</span></span><br />  <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>triangle<span class="token punctuation">"</span></span><br /><span class="token punctuation">/></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span><br />  <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/static/images/LOMMHkSlRo-200.jpeg<span class="token punctuation">"</span></span><br />  <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>200<span class="token punctuation">"</span></span><br />  <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>298<span class="token punctuation">"</span></span><br />  <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>A classic gameboy on a black background clipped into a triangle<span class="token punctuation">"</span></span><br />  <span class="token attr-name">loading</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lazy<span class="token punctuation">"</span></span><br />  <span class="token attr-name">decoding</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>async<span class="token punctuation">"</span></span><br />  <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>triangle<span class="token punctuation">"</span></span><br /><span class="token punctuation">/></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span><br />  Photos by<br />  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span><br />    <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://unsplash.com/@eli_j<span class="token punctuation">"</span></span><br />    <span class="token attr-name">target</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>_blank<span class="token punctuation">"</span></span><br />    <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>external noopener noreferrer<span class="token punctuation">"</span></span><br />    <span class="token punctuation">></span></span>Elias Castillo<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><br />  <span class="token punctuation">></span></span><br />  and<br />  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span><br />    <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://unsplash.com/@helloimnik<span class="token punctuation">"</span></span><br />    <span class="token attr-name">target</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>_blank<span class="token punctuation">"</span></span><br />    <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>external noopener noreferrer<span class="token punctuation">"</span></span><br />    <span class="token punctuation">></span></span>Hello I'm Nik<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><br />  <span class="token punctuation">></span></span><br />  on Unsplash<br /><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span></code></pre>
</details><details class="eleventy-plugin-embedded-demo__code" false="">
  <summary class="eleventy-plugin-embedded-demo__code-toggle">CSS</summary>
  <pre class="language-css"><code class="language-css"><span class="token selector">.triangle</span> <span class="token punctuation">{</span><br />  <span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>50% 0%<span class="token punctuation">,</span> 100% 100%<span class="token punctuation">,</span> 0% 100%<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
</details>
  <details class="eleventy-plugin-embedded-demo__result" open="">
    <summary class="eleventy-plugin-embedded-demo__result-toggle">Result</summary>
    <iframe src="https://codefoodpixels.com/demo/clip-path/basic-percentages/" title="Basic clip-path demo with percentages" loading="lazy" class="eleventy-plugin-embedded-demo__result-iframe"></iframe>
  </details>
</div>
<p>We can also combine fixed units and percentages to achieve a balance between the look that we want and flexability.</p>
<div class="eleventy-plugin-embedded-demo__container">
  <details class="eleventy-plugin-embedded-demo__code" false="">
  <summary class="eleventy-plugin-embedded-demo__code-toggle">HTML</summary>
  <pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span><br />  <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/static/images/jPB8yppx1f-300.jpeg<span class="token punctuation">"</span></span><br />  <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>300<span class="token punctuation">"</span></span><br />  <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>200<span class="token punctuation">"</span></span><br />  <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>A green gameboy color held in a hand and clipped into an arrow<span class="token punctuation">"</span></span><br />  <span class="token attr-name">loading</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lazy<span class="token punctuation">"</span></span><br />  <span class="token attr-name">decoding</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>async<span class="token punctuation">"</span></span><br />  <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>clip<span class="token punctuation">"</span></span><br /><span class="token punctuation">/></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span><br />  <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/static/images/LOMMHkSlRo-200.jpeg<span class="token punctuation">"</span></span><br />  <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>200<span class="token punctuation">"</span></span><br />  <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>298<span class="token punctuation">"</span></span><br />  <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>A classic gameboy on a black background clipped into an arrow<span class="token punctuation">"</span></span><br />  <span class="token attr-name">loading</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lazy<span class="token punctuation">"</span></span><br />  <span class="token attr-name">decoding</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>async<span class="token punctuation">"</span></span><br />  <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>clip<span class="token punctuation">"</span></span><br /><span class="token punctuation">/></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span><br />  Photos by<br />  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span><br />    <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://unsplash.com/@eli_j<span class="token punctuation">"</span></span><br />    <span class="token attr-name">target</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>_blank<span class="token punctuation">"</span></span><br />    <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>external noopener noreferrer<span class="token punctuation">"</span></span><br />    <span class="token punctuation">></span></span>Elias Castillo<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><br />  <span class="token punctuation">></span></span><br />  and<br />  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span><br />    <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://unsplash.com/@helloimnik<span class="token punctuation">"</span></span><br />    <span class="token attr-name">target</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>_blank<span class="token punctuation">"</span></span><br />    <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>external noopener noreferrer<span class="token punctuation">"</span></span><br />    <span class="token punctuation">></span></span>Hello I'm Nik<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><br />  <span class="token punctuation">></span></span><br />  on Unsplash<br /><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span></code></pre>
</details><details class="eleventy-plugin-embedded-demo__code" false="">
  <summary class="eleventy-plugin-embedded-demo__code-toggle">CSS</summary>
  <pre class="language-css"><code class="language-css"><span class="token selector">.clip</span> <span class="token punctuation">{</span><br />  <span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>0px 0px<span class="token punctuation">,</span> 100% 50%<span class="token punctuation">,</span> 0px 100%<span class="token punctuation">,</span> 50px 50%<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
</details>
  <details class="eleventy-plugin-embedded-demo__result" open="">
    <summary class="eleventy-plugin-embedded-demo__result-toggle">Result</summary>
    <iframe src="https://codefoodpixels.com/demo/clip-path/combined/" title="Clip-path demo with percentages and fixed units" loading="lazy" class="eleventy-plugin-embedded-demo__result-iframe"></iframe>
  </details>
</div>
<p>Finally, using the CSS <code>calc</code> function means that we can achieve offsets from each edge while still staying flexible to different shapes and sizes of image.</p>
<div class="eleventy-plugin-embedded-demo__container">
  <details class="eleventy-plugin-embedded-demo__code" false="">
  <summary class="eleventy-plugin-embedded-demo__code-toggle">HTML</summary>
  <pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span><br />  <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/static/images/t5vV7unhLw-300.jpeg<span class="token punctuation">"</span></span><br />  <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>300<span class="token punctuation">"</span></span><br />  <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>200<span class="token punctuation">"</span></span><br />  <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>A clear gameboy and 3 games clipped into a triangle<span class="token punctuation">"</span></span><br />  <span class="token attr-name">loading</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lazy<span class="token punctuation">"</span></span><br />  <span class="token attr-name">decoding</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>async<span class="token punctuation">"</span></span><br />  <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>clip<span class="token punctuation">"</span></span><br /><span class="token punctuation">/></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span><br />  <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/static/images/t5vV7unhLw-600.jpeg<span class="token punctuation">"</span></span><br />  <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>600<span class="token punctuation">"</span></span><br />  <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>400<span class="token punctuation">"</span></span><br />  <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>A clear gameboy and 3 games clipped into a triangle<span class="token punctuation">"</span></span><br />  <span class="token attr-name">loading</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lazy<span class="token punctuation">"</span></span><br />  <span class="token attr-name">decoding</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>async<span class="token punctuation">"</span></span><br />  <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>clip<span class="token punctuation">"</span></span><br /><span class="token punctuation">/></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span><br />  Photo by<br />  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span><br />    <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://unsplash.com/@dimhou<span class="token punctuation">"</span></span><br />    <span class="token attr-name">target</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>_blank<span class="token punctuation">"</span></span><br />    <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>external noopener noreferrer<span class="token punctuation">"</span></span><br />    <span class="token punctuation">></span></span>Dim Hou<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><br />  <span class="token punctuation">></span></span><br />  on Unsplash<br /><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span></code></pre>
</details><details class="eleventy-plugin-embedded-demo__code" false="">
  <summary class="eleventy-plugin-embedded-demo__code-toggle">CSS</summary>
  <pre class="language-css"><code class="language-css"><span class="token selector">.clip</span> <span class="token punctuation">{</span><br />  <span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span><br />    0px 30px<span class="token punctuation">,</span><br />    30px 0px<span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 30px<span class="token punctuation">)</span> 0px<span class="token punctuation">,</span><br />    100% 30px<span class="token punctuation">,</span><br />    100% <span class="token function">calc</span><span class="token punctuation">(</span>100% - 30px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 30px<span class="token punctuation">)</span> 100%<span class="token punctuation">,</span><br />    30px 100%<span class="token punctuation">,</span><br />    0px <span class="token function">calc</span><span class="token punctuation">(</span>100% - 30px<span class="token punctuation">)</span><br />  <span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
</details>
  <details class="eleventy-plugin-embedded-demo__result" open="">
    <summary class="eleventy-plugin-embedded-demo__result-toggle">Result</summary>
    <iframe src="https://codefoodpixels.com/demo/clip-path/combined-calc/" title="Basic clip-path demo" loading="lazy" class="eleventy-plugin-embedded-demo__result-iframe"></iframe>
  </details>
</div>
<p>So now that we have all of the bits of knowledge that we have for the CSS side, lets look at the corner itself.</p>
<p>Below there is an example of a pixel art curve, with outlines added for emphasis. As you can see, it's made of a number of &quot;blocks&quot; (called pixels) and each of these is placed in a certain way to give the idea of a curve. We need to replicate this shape with our CSS <code>clip-path</code>.</p>
<img alt="A diagram of a pixel art rounded corner" loading="lazy" decoding="async" class="image__no-clip-path" src="https://codefoodpixels.com/static/images/Gqyh6vxN6V-400.svg" width="400" height="400" />
<p>To replicate the shape, we need to create a set of points that follow the outside of the pixels. If we start from the leftmost point that we need to define, we have <code>x</code> being <code>0px</code> and <code>y</code> being <code>5px</code>. We then need to step inwards one pixel to create the top of the pixel, so for our next point we have <code>x</code> being <code>1px</code> and <code>y</code> still being <code>5px</code>. Our next step is to follow the pixels up, so we have <code>x</code> still as <code>1px</code>, but <code>y</code> is now <code>3px</code>.</p>
<p>After repeating this to follow the entire curve, we get the following set of coordinates:</p>
<pre class="language-css"><code class="language-css"><span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span><br />  0px 5px<span class="token punctuation">,</span><br />  1px 5px<span class="token punctuation">,</span><br />  1px 3px<span class="token punctuation">,</span><br />  2px 3px<span class="token punctuation">,</span><br />  2px 2px<span class="token punctuation">,</span><br />  3px 2px<span class="token punctuation">,</span><br />  3px 1px<span class="token punctuation">,</span><br />  4px 1px<span class="token punctuation">,</span><br />  5px 1px<span class="token punctuation">,</span><br />  5px 0px<br /><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>This gives us the top left corner. By using <code>calc()</code> and flipping horizontally and vertically, we can create the points for the other 4 corners.</p>
<p>As you can see in the below demo, while it compares well to the <code>border-radius</code> example, it's not very noticable at the current scale because we're doing it on a single pixel basis.</p>
<div class="eleventy-plugin-embedded-demo__container">
  <details class="eleventy-plugin-embedded-demo__code" false="">
  <summary class="eleventy-plugin-embedded-demo__code-toggle">HTML</summary>
  <pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span><br />  <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/static/images/t5vV7unhLw-300.jpeg<span class="token punctuation">"</span></span><br />  <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>300<span class="token punctuation">"</span></span><br />  <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>200<span class="token punctuation">"</span></span><br />  <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>A clear gameboy and 3 games with pixelated rounded corners<span class="token punctuation">"</span></span><br />  <span class="token attr-name">loading</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lazy<span class="token punctuation">"</span></span><br />  <span class="token attr-name">decoding</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>async<span class="token punctuation">"</span></span><br />  <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>clip<span class="token punctuation">"</span></span><br /><span class="token punctuation">/></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span><br />  <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/static/images/t5vV7unhLw-300.jpeg<span class="token punctuation">"</span></span><br />  <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>300<span class="token punctuation">"</span></span><br />  <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>200<span class="token punctuation">"</span></span><br />  <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>A clear gameboy and 3 games with rounded corners<span class="token punctuation">"</span></span><br />  <span class="token attr-name">loading</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lazy<span class="token punctuation">"</span></span><br />  <span class="token attr-name">decoding</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>async<span class="token punctuation">"</span></span><br />  <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>radius<span class="token punctuation">"</span></span><br /><span class="token punctuation">/></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span><br />  Photo by<br />  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span><br />    <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://unsplash.com/@dimhou<span class="token punctuation">"</span></span><br />    <span class="token attr-name">target</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>_blank<span class="token punctuation">"</span></span><br />    <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>external noopener noreferrer<span class="token punctuation">"</span></span><br />    <span class="token punctuation">></span></span>Dim Hou<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><br />  <span class="token punctuation">></span></span><br />  on Unsplash<br /><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span></code></pre>
</details><details class="eleventy-plugin-embedded-demo__code" false="">
  <summary class="eleventy-plugin-embedded-demo__code-toggle">CSS</summary>
  <pre class="language-css"><code class="language-css"><span class="token selector">.clip</span> <span class="token punctuation">{</span><br />  <span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span><br />    0px 5px<span class="token punctuation">,</span><br />    1px 5px<span class="token punctuation">,</span><br />    1px 3px<span class="token punctuation">,</span><br />    2px 3px<span class="token punctuation">,</span><br />    2px 2px<span class="token punctuation">,</span><br />    3px 2px<span class="token punctuation">,</span><br />    3px 1px<span class="token punctuation">,</span><br />    4px 1px<span class="token punctuation">,</span><br />    5px 1px<span class="token punctuation">,</span><br />    5px 0px<span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 5px<span class="token punctuation">)</span> 0px<span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 5px<span class="token punctuation">)</span> 1px<span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 3px<span class="token punctuation">)</span> 1px<span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 3px<span class="token punctuation">)</span> 2px<span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 2px<span class="token punctuation">)</span> 2px<span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 2px<span class="token punctuation">)</span> 3px<span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 1px<span class="token punctuation">)</span> 3px<span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 1px<span class="token punctuation">)</span> 4px<span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 1px<span class="token punctuation">)</span> 5px<span class="token punctuation">,</span><br />    100% 5px<span class="token punctuation">,</span><br />    100% <span class="token function">calc</span><span class="token punctuation">(</span>100% - 5px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 1px<span class="token punctuation">)</span> <span class="token function">calc</span><span class="token punctuation">(</span>100% - 5px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 1px<span class="token punctuation">)</span> <span class="token function">calc</span><span class="token punctuation">(</span>100% - 3px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 2px<span class="token punctuation">)</span> <span class="token function">calc</span><span class="token punctuation">(</span>100% - 3px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 2px<span class="token punctuation">)</span> <span class="token function">calc</span><span class="token punctuation">(</span>100% - 2px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 3px<span class="token punctuation">)</span> <span class="token function">calc</span><span class="token punctuation">(</span>100% - 2px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 3px<span class="token punctuation">)</span> <span class="token function">calc</span><span class="token punctuation">(</span>100% - 1px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 4px<span class="token punctuation">)</span> <span class="token function">calc</span><span class="token punctuation">(</span>100% - 1px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 5px<span class="token punctuation">)</span> <span class="token function">calc</span><span class="token punctuation">(</span>100% - 1px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 5px<span class="token punctuation">)</span> 100%<span class="token punctuation">,</span><br />    5px 100%<span class="token punctuation">,</span><br />    5px <span class="token function">calc</span><span class="token punctuation">(</span>100% - 1px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    3px <span class="token function">calc</span><span class="token punctuation">(</span>100% - 1px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    3px <span class="token function">calc</span><span class="token punctuation">(</span>100% - 2px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    2px <span class="token function">calc</span><span class="token punctuation">(</span>100% - 2px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    2px <span class="token function">calc</span><span class="token punctuation">(</span>100% - 3px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    1px <span class="token function">calc</span><span class="token punctuation">(</span>100% - 3px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    1px <span class="token function">calc</span><span class="token punctuation">(</span>100% - 4px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    1px <span class="token function">calc</span><span class="token punctuation">(</span>100% - 5px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    0px <span class="token function">calc</span><span class="token punctuation">(</span>100% - 5px<span class="token punctuation">)</span><br />  <span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><br /><span class="token selector">.radius</span> <span class="token punctuation">{</span><br />  <span class="token property">border-radius</span><span class="token punctuation">:</span> 8px<span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
</details>
  <details class="eleventy-plugin-embedded-demo__result" open="">
    <summary class="eleventy-plugin-embedded-demo__result-toggle">Result</summary>
    <iframe src="https://codefoodpixels.com/demo/clip-path/rounded-corners/" title="Rounded corners clip-path demo" loading="lazy" class="eleventy-plugin-embedded-demo__result-iframe"></iframe>
  </details>
</div>
<p>To make it clearer, we have to scale the pixels up. I personally use a scaling multiplier of 4, so 4 on-screen pixels is one pixel in the design. To actually implement this, we take the fixed values from the previous example and multiply them by our scaling multiplier, so the top left corner would be the following:</p>
<pre class="language-css"><code class="language-css"><span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span><br />  0px 20px<span class="token punctuation">,</span><br />  4px 20px<span class="token punctuation">,</span><br />  4px 12px<span class="token punctuation">,</span><br />  8px 12px<span class="token punctuation">,</span><br />  8px 8px<span class="token punctuation">,</span><br />  12px 8px<span class="token punctuation">,</span><br />  12px 4px<span class="token punctuation">,</span><br />  16px 4px<span class="token punctuation">,</span><br />  20px 4px<span class="token punctuation">,</span><br />  20px 0px<br /><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>After applying this to the rest of the corners, it looks like the below demo:</p>
<div class="eleventy-plugin-embedded-demo__container">
  <details class="eleventy-plugin-embedded-demo__code" false="">
  <summary class="eleventy-plugin-embedded-demo__code-toggle">HTML</summary>
  <pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span><br />  <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/static/images/t5vV7unhLw-300.jpeg<span class="token punctuation">"</span></span><br />  <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>300<span class="token punctuation">"</span></span><br />  <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>200<span class="token punctuation">"</span></span><br />  <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>A clear gameboy and 3 games with pixelated rounded corners<span class="token punctuation">"</span></span><br />  <span class="token attr-name">loading</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lazy<span class="token punctuation">"</span></span><br />  <span class="token attr-name">decoding</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>async<span class="token punctuation">"</span></span><br />  <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>clip<span class="token punctuation">"</span></span><br /><span class="token punctuation">/></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span><br />  Photo by<br />  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span><br />    <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://unsplash.com/@dimhou<span class="token punctuation">"</span></span><br />    <span class="token attr-name">target</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>_blank<span class="token punctuation">"</span></span><br />    <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>external noopener noreferrer<span class="token punctuation">"</span></span><br />    <span class="token punctuation">></span></span>Dim Hou<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><br />  <span class="token punctuation">></span></span><br />  on Unsplash<br /><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span></code></pre>
</details><details class="eleventy-plugin-embedded-demo__code" false="">
  <summary class="eleventy-plugin-embedded-demo__code-toggle">CSS</summary>
  <pre class="language-css"><code class="language-css"><span class="token selector">.clip</span> <span class="token punctuation">{</span><br />  <span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span><br />    0px 20px<span class="token punctuation">,</span><br />    4px 20px<span class="token punctuation">,</span><br />    4px 12px<span class="token punctuation">,</span><br />    8px 12px<span class="token punctuation">,</span><br />    8px 8px<span class="token punctuation">,</span><br />    12px 8px<span class="token punctuation">,</span><br />    12px 4px<span class="token punctuation">,</span><br />    16px 4px<span class="token punctuation">,</span><br />    20px 4px<span class="token punctuation">,</span><br />    20px 0px<span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 20px<span class="token punctuation">)</span> 0px<span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 20px<span class="token punctuation">)</span> 4px<span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 12px<span class="token punctuation">)</span> 4px<span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 12px<span class="token punctuation">)</span> 8px<span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 8px<span class="token punctuation">)</span> 8px<span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 8px<span class="token punctuation">)</span> 12px<span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 4px<span class="token punctuation">)</span> 12px<span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 4px<span class="token punctuation">)</span> 16px<span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 4px<span class="token punctuation">)</span> 20px<span class="token punctuation">,</span><br />    100% 20px<span class="token punctuation">,</span><br />    100% <span class="token function">calc</span><span class="token punctuation">(</span>100% - 20px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 4px<span class="token punctuation">)</span> <span class="token function">calc</span><span class="token punctuation">(</span>100% - 20px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 4px<span class="token punctuation">)</span> <span class="token function">calc</span><span class="token punctuation">(</span>100% - 12px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 8px<span class="token punctuation">)</span> <span class="token function">calc</span><span class="token punctuation">(</span>100% - 12px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 8px<span class="token punctuation">)</span> <span class="token function">calc</span><span class="token punctuation">(</span>100% - 8px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 12px<span class="token punctuation">)</span> <span class="token function">calc</span><span class="token punctuation">(</span>100% - 8px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 12px<span class="token punctuation">)</span> <span class="token function">calc</span><span class="token punctuation">(</span>100% - 4px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 16px<span class="token punctuation">)</span> <span class="token function">calc</span><span class="token punctuation">(</span>100% - 4px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 20px<span class="token punctuation">)</span> <span class="token function">calc</span><span class="token punctuation">(</span>100% - 4px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    <span class="token function">calc</span><span class="token punctuation">(</span>100% - 20px<span class="token punctuation">)</span> 100%<span class="token punctuation">,</span><br />    20px 100%<span class="token punctuation">,</span><br />    20px <span class="token function">calc</span><span class="token punctuation">(</span>100% - 4px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    12px <span class="token function">calc</span><span class="token punctuation">(</span>100% - 4px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    12px <span class="token function">calc</span><span class="token punctuation">(</span>100% - 8px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    8px <span class="token function">calc</span><span class="token punctuation">(</span>100% - 8px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    8px <span class="token function">calc</span><span class="token punctuation">(</span>100% - 12px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    4px <span class="token function">calc</span><span class="token punctuation">(</span>100% - 12px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    4px <span class="token function">calc</span><span class="token punctuation">(</span>100% - 16px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    4px <span class="token function">calc</span><span class="token punctuation">(</span>100% - 20px<span class="token punctuation">)</span><span class="token punctuation">,</span><br />    0px <span class="token function">calc</span><span class="token punctuation">(</span>100% - 20px<span class="token punctuation">)</span><br />  <span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
</details>
  <details class="eleventy-plugin-embedded-demo__result" open="">
    <summary class="eleventy-plugin-embedded-demo__result-toggle">Result</summary>
    <iframe src="https://codefoodpixels.com/demo/clip-path/rounded-corners-scaled/" title="Scaled rounded corners clip-path demo" loading="lazy" class="eleventy-plugin-embedded-demo__result-iframe"></iframe>
  </details>
</div>
<p>We have some nice pixelated rounded corners! I think it looks great and fits in really well on my site. This technique can be used for all sorts of shapes though, and it doesn't have to be pixelated.</p>
<p>But if you do want to do pixelated rounded corners, you can save yourself the effort and use <a href="https://pixelcorners.lukeb.co.uk/" target="_blank" rel="external noopener noreferrer">the generator that I created</a> after I'd done this on my own site. With the generator you can choose your own pixel multiplier and radius rather than using the ones I've defined here, and you'll get a live preview!</p>

    ]]></content>
  </entry>
	
  
  <entry>
    <title>Per aspera ad astra</title>
    <link href="https://codefoodpixels.com/blog/2021/05/12/per-aspera-ad-astra/"/>
    <updated>2021-05-12T10:30:00Z</updated>
    <id>https://codefoodpixels.com/blog/2021/05/12/per-aspera-ad-astra/</id>
    <content type="html"><![CDATA[
      <p>When I was younger, I wanted to be a &quot;robot scientist&quot;. I found them fascinating and wanted to learn about how they work and how to build them, but I lived in the middle of nowhere, in a time when the web was still finding it's feet, which meant that the resources weren't really there for me and eventually I realised that it wasn't really a viable option.</p>
<p>In my early teens, I found web development and I've been hooked ever since. Open source was, and still is, massively helpful for me to learn from. Whether that's taking apart open source projects to learn how they work, or contributing to them.</p>
<p>Recently, I found out that these two worlds collided.</p>
<p>I left school in 2008, right into the Great Recession. I wanted to get into web development as a career, but with no industry experience and no qualifications, it was difficult. After being unemployed for 9 months, I got a job as an IT Administrator. Attempt 0 at making web development a career was unsuccessful.</p>
<p>After a couple of years working as an IT Administrator and learning more about web development in my own time, I decided it was time to make another attempt at making a career as a web developer. I interviewed at a few companies and got 2 job offers, I accepted a junior developer role at a design agency and started my career as a web developer. Unfortunately I didn't really get any support, was given clients to handle on my own and <a href="https://codefoodpixels.com/blog/2018/09/07/fired/">ultimately was let go after 15 months due to not meeting their (unrealistic) expectations</a>. Career attempt 1 was unsuccessful.</p>
<p>Among other things, I spent some time learning and writing some open source projects. After about 7 months, I started another development job and that was the start of career attempt 2. It was a much more supportive enviornment. I learned about community groups such as LeedsJS, I started writing and maintaining more open source projects, and started building things in public. That was back in 2013, and I'm still going.</p>
<p>In the years since, I've been building things and releasing them as open source projects, whether that's for people to use themselves, or for them to learn from, like I did. I've also contributed back to projects that I've used and found helpful.</p>
<p>And recently, one of those projects was used as part of a major scientific mission: The Mars 2020 Helicopter, Ingenuity.</p>
<p><picture><source type="image/avif" srcset="https://codefoodpixels.com/static/images/jbiIJHK--r-250.avif 250w, https://codefoodpixels.com/static/images/jbiIJHK--r-600.avif 600w, https://codefoodpixels.com/static/images/jbiIJHK--r-800.avif 800w, https://codefoodpixels.com/static/images/jbiIJHK--r-1000.avif 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/webp" srcset="https://codefoodpixels.com/static/images/jbiIJHK--r-250.webp 250w, https://codefoodpixels.com/static/images/jbiIJHK--r-600.webp 600w, https://codefoodpixels.com/static/images/jbiIJHK--r-800.webp 800w, https://codefoodpixels.com/static/images/jbiIJHK--r-1000.webp 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/jpeg" srcset="https://codefoodpixels.com/static/images/jbiIJHK--r-250.jpeg 250w, https://codefoodpixels.com/static/images/jbiIJHK--r-600.jpeg 600w, https://codefoodpixels.com/static/images/jbiIJHK--r-800.jpeg 800w, https://codefoodpixels.com/static/images/jbiIJHK--r-1000.jpeg 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/png" srcset="https://codefoodpixels.com/static/images/jbiIJHK--r-250.png 250w, https://codefoodpixels.com/static/images/jbiIJHK--r-600.png 600w, https://codefoodpixels.com/static/images/jbiIJHK--r-800.png 800w, https://codefoodpixels.com/static/images/jbiIJHK--r-1000.png 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><img alt="A popover from the GitHub website that says that my contribution to Bootstrap was used as part of the Mars 2020 helicopter mission" loading="lazy" decoding="async" src="https://codefoodpixels.com/static/images/jbiIJHK--r-250.jpeg" width="1000" height="413" /></picture></p>
<p>Bootstrap 4 was used, and <a href="https://github.com/twbs/bootstrap/commit/319009a72b8889d8db5b84ad69ce4118cdf3143f" target="_blank" rel="external noopener noreferrer">5 lines of code that I contributed</a> were part of that. It's only a small contribution, but it shows how wide reaching open source code can be. My code is on Mars!</p>
<p>This post is titled &quot;Per aspera ad astra&quot;, a phrase which is part of one of my tattoos. It translates from latin as &quot;through adversity to the stars&quot;, and it's pretty cool that this can now relate to my web development career. I had the adversity of getting into development as a career and the rocky start of getting fired from my first development job, and now my code is part of a mission to another planet.</p>
<p>It's mind blowing 🤯</p>

    ]]></content>
  </entry>
	
  
  <entry>
    <title>No comment: Adding Webmentions to my site</title>
    <link href="https://codefoodpixels.com/blog/2021/03/15/no-comment-adding-webmentions-to-my-site/"/>
    <updated>2021-03-15T10:30:00Z</updated>
    <id>https://codefoodpixels.com/blog/2021/03/15/no-comment-adding-webmentions-to-my-site/</id>
    <content type="html"><![CDATA[
      <p>While I was <a href="https://codefoodpixels.com/blog/2021/02/12/now-with-added-eleventy">rebuilding my website in Eleventy</a>, I saw Amber Wilson's <a href="https://amberwilson.co.uk/blog/grow-the-indieweb-with-webmentions/" target="_blank" rel="external noopener noreferrer">article about adding Webmentions to her site</a> shared on Twitter. I'd heard of Webmentions but I'd never really looked into them and while I was reading Amber's post, I thought that it's a really cool idea and added them to the list of things I wanted to add to my site.</p>
<h2 id="whats-a-webmention">What's a Webmention?</h2>
<p>A Webmention is a way to let a website know that it's been mentioned by someone, somewhere on the web.</p>
<p>As an example: if I write a blog post and someone finds it interesting, then they can write their own blog post linking to mine and their website's software could send me a Webmention. I can then take that Webmention and display it on my website with a link to their article.</p>
<p>In fact, by linking to Amber's post above, I've sent her a Webmention. Cool, right?</p>
<p>Webmentions are a <a href="https://www.w3.org/TR/webmention/" target="_blank" rel="external noopener noreferrer">W3C Recommendation</a> and is part of the <a href="https://indieweb.org/" target="_blank" rel="external noopener noreferrer">IndieWeb movement</a>. It's basically pingback but reimplemented without all the XML mess, just a POST request with the source URL (page that mentions the post) and the target URL (the post being mentioned).</p>
<h2 id="ok-how-do-add-webmentions-to-my-site">Ok, how do add Webmentions to my site?</h2>
<p>From a high level, there are 3 steps you need to take to add Webmentions to your site:</p>
<ol>
<li><a href="https://codefoodpixels.com/blog/2021/03/15/no-comment-adding-webmentions-to-my-site/#declaring-an-endpoint-so-that-your-site-can-recieve-webmentions">Declare an endpoint so that your site can recieve Webmentions</a></li>
<li><a href="https://codefoodpixels.com/blog/2021/03/15/no-comment-adding-webmentions-to-my-site/#displaying-webmentions-on-your-website">Display Webmentions within your site</a></li>
<li><a href="https://codefoodpixels.com/blog/2021/03/15/no-comment-adding-webmentions-to-my-site/#sending-webmentions-to-other-sites">Send Webmentions to other sites</a></li>
</ol>
<p>So we'll go through these stages, and I'll talk about how I integrated it into my website with <a href="https://11ty.dev/" target="_blank" rel="external noopener noreferrer">Eleventy</a>.</p>
<h3 id="declaring-an-endpoint-so-that-your-site-can-recieve-webmentions">Declaring an endpoint so that your site can recieve Webmentions</h3>
<p>To be able to recieve webmentions, you need to declare an endpoint. In his <a href="https://adactio.com/journal/6495" target="_blank" rel="external noopener noreferrer">Parsing Webmentions post</a>, Jeremy Keith talks about building a minimum viable webmention endpoint in PHP. It's definitely worth a look if you really want to build something, but otherwise you should use a service like <a href="https://webmention.io/" target="_blank" rel="external noopener noreferrer">Webmention.io</a>, which is what I did.</p>
<p>To sign into Webmention.io, you'll need to <a href="https://indieweb.org/How_to_set_up_web_sign-in_on_your_own_domain#2._Link_to_your_home_page_from_each_service" target="_blank" rel="external noopener noreferrer">set up web sign-in on your website</a>. I did this by adding <code>rel=&quot;me&quot;</code> to the Twitter and GitHub links in my navigation, and ensuring that my Twitter and GitHub profiles link to my website.</p>
<p>Once signed in, you can find the tags you'll need to add to your <code>head</code> tag that will tell other sites where to send webmentions. If you can't find them straight away, try going to the <a href="https://webmention.io/settings" target="_blank" rel="external noopener noreferrer">settings</a> page.</p>
<p>In my case, the tags look like this:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>webmention<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://webmention.io/codefoodpixels.com/webmention<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pingback<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://webmention.io/codefoodpixels.com/xmlrpc<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></code></pre>
<p>Now your website can get Webmentions!</p>
<h3 id="displaying-webmentions-on-your-website">Displaying Webmentions on your website</h3>
<p>So now that we're gathering Webmentions, we need to show them somewhere. The way that you do this entirely depends on how your website is built and how you gathered your webmentions, I'll be writing this with Webmention.io and Eleventy in mind but some of this will be transferrable.</p>
<h4 id="fetching-webmentions">Fetching webmentions</h4>
<p>To grab your Webmentions, <a href="https://github.com/aaronpk/webmention.io#api" target="_blank" rel="external noopener noreferrer">Webmention.io has an API</a> that returns data as JSON. Through the API, you can request all the mentions for your domain, and the mentions for specific pages. With Eleventy, we can grab this data and and display it nicely in our pages.</p>
<p>Eleventy has data files, and you can use <a href="https://www.11ty.dev/docs/data-js/" target="_blank" rel="external noopener noreferrer">JavaScript data files</a> to do some processing at build time. This means that we can do a call to Webmention.io and grab all of our Webmentions before we process any of the website content. An example of how we can do this is:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> fetch <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"node-fetch"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token keyword">const</span> <span class="token constant">WEBMENTION_BASE_URL</span> <span class="token operator">=</span> <span class="token string">"https://webmention.io/api/mentions.jf2"</span><span class="token punctuation">;</span><br /><br />module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br />  <span class="token keyword">const</span> domain <span class="token operator">=</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">DOMAIN</span><span class="token punctuation">;</span> <span class="token comment">// e.g. lukeb.co.uk</span><br />  <span class="token keyword">const</span> token <span class="token operator">=</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">WEBMENTION_IO_TOKEN</span><span class="token punctuation">;</span> <span class="token comment">// found at the bottom of https://webmention.io/settings</span><br /><br />  <span class="token keyword">const</span> url <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token constant">WEBMENTION_BASE_URL</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">?domain=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>domain<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&amp;token=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>token<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&amp;per-page=1000</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span><br /><br />  <span class="token keyword">try</span> <span class="token punctuation">{</span><br />    <span class="token keyword">const</span> res <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">fetch</span><span class="token punctuation">(</span>url<span class="token punctuation">)</span><span class="token punctuation">;</span><br />    <span class="token keyword">if</span> <span class="token punctuation">(</span>res<span class="token punctuation">.</span>ok<span class="token punctuation">)</span> <span class="token punctuation">{</span><br />      <span class="token keyword">const</span> feed <span class="token operator">=</span> <span class="token keyword">await</span> res<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br />      <span class="token keyword">return</span> feed<span class="token punctuation">.</span>children<span class="token punctuation">;</span><br />    <span class="token punctuation">}</span><br />  <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span><br />    console<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">;</span><br />    <span class="token keyword">return</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br />  <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>If we save the above as <code>webmentions.js</code> within the <code>_data</code> folder of our Eleventy project, then we'll have an array of the 1000 most recent Webmentions for our website available under the <code>webmentions</code> key in all of our templates.</p>
<h4 id="filtering-and-rendering">Filtering and rendering</h4>
<p>We've grabbed the data, but it's just one big array for the whole domain. We need to filter this array so that we only get the mentions for the page that we're currently rendering. Within that data, we also have different types that we probably want to separate out into groups, such as likes, reposts and replies.</p>
<p>The different types of Webmentions supported by Webmention.io are:</p>
<ul>
<li><code>in-reply-to</code></li>
<li><code>like-of</code></li>
<li><code>repost-of</code></li>
<li><code>bookmark-of</code></li>
<li><code>mention-of</code></li>
<li><code>rsvp</code></li>
</ul>
<p>For my site, I'll be using <code>in-reply-to</code>, <code>mention-of</code>, <code>like-of</code> and <code>repost-of</code>, with <code>in-reply-to</code> and <code>mention-of</code> grouped together as <code>comments</code>.</p>
<p>The steps we'll be going through are:</p>
<ul>
<li>Filter to get Webmentions for the current page, and of the types of mention that we want</li>
<li>Sort the Webmentions by publish date</li>
<li>Truncate any long mentions</li>
</ul>
<p>That would look something like this:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> <span class="token punctuation">{</span> <span class="token constant">URL</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"url"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">function</span> <span class="token function">webmentionsForPage</span><span class="token punctuation">(</span><span class="token parameter">webmentions<span class="token punctuation">,</span> page</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br />  <span class="token keyword">const</span> url <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">URL</span><span class="token punctuation">(</span>page<span class="token punctuation">,</span> <span class="token string">"https://lukeb.co.uk/"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />  <span class="token keyword">const</span> allowedTypes <span class="token operator">=</span> <span class="token punctuation">{</span><br />    <span class="token literal-property property">likes</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"like-of"</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br />    <span class="token literal-property property">reposts</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"repost-of"</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br />    <span class="token literal-property property">comments</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"mention-of"</span><span class="token punctuation">,</span> <span class="token string">"in-reply-to"</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br />  <span class="token punctuation">}</span><span class="token punctuation">;</span><br /><br />  <span class="token keyword">const</span> <span class="token function-variable function">clean</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">entry</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br />    <span class="token keyword">if</span> <span class="token punctuation">(</span>entry<span class="token punctuation">.</span>content<span class="token punctuation">)</span> <span class="token punctuation">{</span><br />      <span class="token keyword">if</span> <span class="token punctuation">(</span>entry<span class="token punctuation">.</span>content<span class="token punctuation">.</span>text<span class="token punctuation">.</span>length <span class="token operator">></span> <span class="token number">280</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br />        entry<span class="token punctuation">.</span>content<span class="token punctuation">.</span>value <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>entry<span class="token punctuation">.</span>content<span class="token punctuation">.</span>text<span class="token punctuation">.</span><span class="token function">substr</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">280</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&amp;hellip;</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span><br />      <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span><br />        entry<span class="token punctuation">.</span>content<span class="token punctuation">.</span>value <span class="token operator">=</span> entry<span class="token punctuation">.</span>content<span class="token punctuation">.</span>text<span class="token punctuation">;</span><br />      <span class="token punctuation">}</span><br />    <span class="token punctuation">}</span><br />    <span class="token keyword">return</span> entry<span class="token punctuation">;</span><br />  <span class="token punctuation">}</span><span class="token punctuation">;</span><br /><br />  <span class="token keyword">const</span> pageWebmentions <span class="token operator">=</span> webmentions<br />    <span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">mention</span><span class="token punctuation">)</span> <span class="token operator">=></span> mention<span class="token punctuation">[</span><span class="token string">"wm-target"</span><span class="token punctuation">]</span> <span class="token operator">===</span> url<span class="token punctuation">)</span><br />    <span class="token punctuation">.</span><span class="token function">sort</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span>b<span class="token punctuation">.</span>published<span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span>a<span class="token punctuation">.</span>published<span class="token punctuation">)</span><span class="token punctuation">)</span><br />    <span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span>clean<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />  <span class="token keyword">const</span> likes <span class="token operator">=</span> cleanedWebmentions<br />    <span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">mention</span><span class="token punctuation">)</span> <span class="token operator">=></span> allowedTypes<span class="token punctuation">.</span>likes<span class="token punctuation">.</span><span class="token function">includes</span><span class="token punctuation">(</span>mention<span class="token punctuation">[</span><span class="token string">"wm-property"</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span><br />    <span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">like</span><span class="token punctuation">)</span> <span class="token operator">=></span> like<span class="token punctuation">.</span>author<span class="token punctuation">)</span><br />    <span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">like</span><span class="token punctuation">)</span> <span class="token operator">=></span> like<span class="token punctuation">.</span>author<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />  <span class="token keyword">const</span> reposts <span class="token operator">=</span> cleanedWebmentions<br />    <span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">mention</span><span class="token punctuation">)</span> <span class="token operator">=></span> allowedTypes<span class="token punctuation">.</span>reposts<span class="token punctuation">.</span><span class="token function">includes</span><span class="token punctuation">(</span>mention<span class="token punctuation">[</span><span class="token string">"wm-property"</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span><br />    <span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">repost</span><span class="token punctuation">)</span> <span class="token operator">=></span> repost<span class="token punctuation">.</span>author<span class="token punctuation">)</span><br />    <span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">repost</span><span class="token punctuation">)</span> <span class="token operator">=></span> repost<span class="token punctuation">.</span>author<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />  <span class="token keyword">const</span> comments <span class="token operator">=</span> cleanedWebmentions<br />    <span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">mention</span><span class="token punctuation">)</span> <span class="token operator">=></span> allowedTypes<span class="token punctuation">.</span>comments<span class="token punctuation">.</span><span class="token function">includes</span><span class="token punctuation">(</span>mention<span class="token punctuation">[</span><span class="token string">"wm-property"</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span><br />    <span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">comment</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br />      <span class="token keyword">const</span> <span class="token punctuation">{</span> author<span class="token punctuation">,</span> published<span class="token punctuation">,</span> content <span class="token punctuation">}</span> <span class="token operator">=</span> comment<span class="token punctuation">;</span><br />      <span class="token keyword">return</span> author <span class="token operator">&amp;&amp;</span> author<span class="token punctuation">.</span>name <span class="token operator">&amp;&amp;</span> published <span class="token operator">&amp;&amp;</span> content<span class="token punctuation">;</span><br />    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />  <span class="token keyword">return</span> <span class="token punctuation">{</span><br />    likes<span class="token punctuation">,</span><br />    reposts<span class="token punctuation">,</span><br />    comments<span class="token punctuation">,</span><br />  <span class="token punctuation">}</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
<p>This is a long chunk of code, but we can set this up as a <a href="https://www.11ty.dev/docs/filters/#universal-filters" target="_blank" rel="external noopener noreferrer">custom filter</a> in our Eleventy config and then we can use it in our templates.</p>
<p>To display our Webmentions, we can render them with a partial like this:</p>
<pre class="language-njk"><code class="language-njk"><br />{<span class="token operator">%</span><span class="token operator">-</span> set postMentions <span class="token operator">=</span> webmentions <span class="token operator">|</span> webmentionsForPage(page.url) <span class="token operator">-</span><span class="token operator">%</span>}<br /><br /><span class="token operator">&lt;</span>h3<span class="token operator">></span>Likes<span class="token operator">&lt;</span><span class="token operator">/</span>h3<span class="token operator">></span><br /><span class="token operator">&lt;</span>ol<span class="token operator">></span><br />  {<span class="token operator">%</span> <span class="token keyword">for</span> like <span class="token keyword">in</span> postMentions.likes <span class="token operator">%</span>}<br />    <span class="token operator">&lt;</span>li<span class="token operator">></span><br />      <span class="token operator">&lt;</span>a href<span class="token operator">=</span>"{{ like.url }}" target<span class="token operator">=</span>"_blank" rel<span class="token operator">=</span>"external noopener noreferrer"<span class="token operator">></span><br />        <span class="token operator">&lt;</span>img<br />          src<span class="token operator">=</span>"{{ like.photo or '<span class="token operator">/</span>static<span class="token operator">/</span>images<span class="token operator">/</span>webmention<span class="token operator">-</span>avatar<span class="token operator">-</span>default.svg' }}"<br />          alt<span class="token operator">=</span>"{{ like.name }}"<br />          loading<span class="token operator">=</span>"lazy"<br />          decoding<span class="token operator">=</span>"async"<br />          width<span class="token operator">=</span>"<span class="token number">48</span>"<br />          height<span class="token operator">=</span>"<span class="token number">48</span>"<br />        <span class="token operator">></span><br />      <span class="token operator">&lt;</span><span class="token operator">/</span>a<span class="token operator">></span><br />    <span class="token operator">&lt;</span><span class="token operator">/</span>li<span class="token operator">></span><br />  {<span class="token operator">%</span> <span class="token keyword">endfor</span> <span class="token operator">%</span>}<br /><span class="token operator">&lt;</span><span class="token operator">/</span>ol<span class="token operator">></span><br /><br /><span class="token operator">&lt;</span>h3<span class="token operator">></span>Reposts<span class="token operator">&lt;</span><span class="token operator">/</span>h3<span class="token operator">></span><br /><span class="token operator">&lt;</span>ol<span class="token operator">></span><br />  {<span class="token operator">%</span> <span class="token keyword">for</span> repost <span class="token keyword">in</span> postMentions.reposts <span class="token operator">%</span>}<br />    <span class="token operator">&lt;</span>li<span class="token operator">></span><br />      <span class="token operator">&lt;</span>a href<span class="token operator">=</span>"{{ repost.url }}" target<span class="token operator">=</span>"_blank" rel<span class="token operator">=</span>"external noopener noreferrer"<span class="token operator">></span><br />        <span class="token operator">&lt;</span>img<br />          src<span class="token operator">=</span>"{{ repost.photo or '<span class="token operator">/</span>static<span class="token operator">/</span>images<span class="token operator">/</span>webmention<span class="token operator">-</span>avatar<span class="token operator">-</span>default.svg' }}"<br />          alt<span class="token operator">=</span>"{{ repost.name }}"<br />          loading<span class="token operator">=</span>"lazy"<br />          decoding<span class="token operator">=</span>"async"<br />          width<span class="token operator">=</span>"<span class="token number">48</span>"<br />          height<span class="token operator">=</span>"<span class="token number">48</span>"<br />        <span class="token operator">></span><br />      <span class="token operator">&lt;</span><span class="token operator">/</span>a<span class="token operator">></span><br />    <span class="token operator">&lt;</span><span class="token operator">/</span>li<span class="token operator">></span><br />  {<span class="token operator">%</span> <span class="token keyword">endfor</span> <span class="token operator">%</span>}<br /><span class="token operator">&lt;</span><span class="token operator">/</span>ol<span class="token operator">></span><br /><br /><span class="token operator">&lt;</span>h3<span class="token operator">></span>Comments<span class="token operator">&lt;</span><span class="token operator">/</span>h3<span class="token operator">></span><br /><span class="token operator">&lt;</span>ol<span class="token operator">></span><br />  {<span class="token operator">%</span> <span class="token keyword">for</span> <span class="token keyword">comment</span> <span class="token keyword">in</span> postMentions.comments <span class="token operator">%</span>}<br />    <span class="token operator">&lt;</span>li<span class="token operator">></span><br />      <span class="token operator">&lt;</span>img<br />        src<span class="token operator">=</span>"{{ <span class="token keyword">comment</span>.author.photo or '<span class="token operator">/</span>static<span class="token operator">/</span>images<span class="token operator">/</span>webmention<span class="token operator">-</span>avatar<span class="token operator">-</span>default.svg' }}"<br />        alt<span class="token operator">=</span>"{{ <span class="token keyword">comment</span>.author.name }}"<br />        loading<span class="token operator">=</span>"lazy"<br />        decoding<span class="token operator">=</span>"async"<br />        width<span class="token operator">=</span>"<span class="token number">48</span>"<br />        height<span class="token operator">=</span>"<span class="token number">48</span>"<br />      <span class="token operator">></span><br />      <span class="token operator">&lt;</span>a href<span class="token operator">=</span>"{{ <span class="token keyword">comment</span>.author.url }}" target<span class="token operator">=</span>"_blank" rel<span class="token operator">=</span>"external noopener noreferrer"<span class="token operator">></span><br />        {{ <span class="token keyword">comment</span>.author.name }}<br />      <span class="token operator">&lt;</span><span class="token operator">/</span>a<span class="token operator">></span><br />      <span class="token operator">&lt;</span>time class<span class="token operator">=</span>"dt<span class="token operator">-</span>published" datetime<span class="token operator">=</span>"{{ <span class="token keyword">comment</span>.published }}"<span class="token operator">></span><br />        {{ <span class="token keyword">comment</span>.published  <span class="token operator">|</span> date("YYYY<span class="token operator">-</span>MM<span class="token operator">-</span>DD") }}<br />      <span class="token operator">&lt;</span><span class="token operator">/</span>time<span class="token operator">></span><br />      <span class="token operator">&lt;</span>p<span class="token operator">></span>{{ <span class="token keyword">comment</span>.content.value }}<span class="token operator">&lt;</span><span class="token operator">/</span>p<span class="token operator">></span><br />      <span class="token operator">&lt;</span>p<span class="token operator">></span><br />        <span class="token operator">&lt;</span>a href<span class="token operator">=</span>"{{ <span class="token keyword">comment</span>.url }}" target<span class="token operator">=</span>"_blank" rel<span class="token operator">=</span>"external noopener noreferrer"<span class="token operator">></span><br />          View original post<br />        <span class="token operator">&lt;</span><span class="token operator">/</span>a<span class="token operator">></span><br />      <span class="token operator">&lt;</span><span class="token operator">/</span>p<span class="token operator">></span><br />    <span class="token operator">&lt;</span><span class="token operator">/</span>li<span class="token operator">></span><br />  {<span class="token operator">%</span> <span class="token keyword">endfor</span> <span class="token operator">%</span>}<br /><span class="token operator">&lt;</span><span class="token operator">/</span>ol<span class="token operator">></span><br /></code></pre>
<p>Now we have our webmentions rendering in the page!</p>
<h2 id="sending-webmentions-to-other-sites">Sending webmentions to other sites</h2>
<p><a href="https://remysharp.com/" target="_blank" rel="external noopener noreferrer">Remy Sharp</a> has built a great tool called <a href="https://webmention.app/" target="_blank" rel="external noopener noreferrer">Webmention.app</a> that takes care of sending your outgoing Webmentions. You can pass it a page or RSS feed URL and it'll go through, grab any links and send Webmentions (or pingbacks) to any sites that support it.</p>
<p>The <a href="https://webmention.app/docs" target="_blank" rel="external noopener noreferrer">Webmention.app documentation</a> has a few different ways to integrate it with your website. Originally I used an outgoing webhook within Netlify to get Webmention.app to send out my Webmentions, but I've now released a Netlify build plugin that doesn't rely on the Webmention.app website.</p>
<p>The <a href="https://github.com/CodeFoodPixels/netlify-plugin-webmentions" target="_blank" rel="external noopener noreferrer">Webmentions Netlify build plugin</a> is a wrapper around the tool that runs Webmention.app, but by using the build plugin, you can avoid relying on a third party service. It'll all be done locally within the build!</p>
<h2 id="bonus-using-bridgy-to-gather-social-media-interactions">Bonus: Using Bridgy to gather social media interactions</h2>
<p>Like a lot of people, I share my blog posts on Twitter to spread it a bit more, and I often get interactions such as likes, retweets and replies. Twitter doesn't support Webmentons, but I can use <a href="https://brid.gy/" target="_blank" rel="external noopener noreferrer">Bridgy</a> to monitor Twitter for me and send Webmentions to my site for any interactions or links to my site.</p>
<p>But Bridgy doesn't just support Twitter, it supports a whole host of other social networks like Instagram, Facebook and Mastodon too!</p>
<h2 id="summary">Summary</h2>
<p>I hope this has given you an insight into what Webmentions are, and a rough idea of how to implement them on your site. I originally thought it was going to be super difficult and complex, but there were some great posts, examples and resources that really helped.</p>
<ul>
<li><a href="https://amberwilson.co.uk/blog/grow-the-indieweb-with-webmentions/" target="_blank" rel="external noopener noreferrer">Amber Wilson's post &quot;Grow the IndieWeb with Webmentions</a></li>
<li><a href="https://mxb.dev/blog/using-webmentions-on-static-sites/" target="_blank" rel="external noopener noreferrer">Max Böck's post &quot;Using Webmentions in Eleventy&quot;</a></li>
<li><a href="https://indieweb.org/Webmention-developer#IndieWeb_Examples" target="_blank" rel="external noopener noreferrer">IndieWeb examples on IndieWeb.org</a></li>
</ul>
<h3 id="update-2022-06-28">Update 2022-06-28</h3>
<p>I've now published a reworked and refined version of this <a href="https://www.npmjs.com/package/eleventy-plugin-webmentions" target="_blank" rel="external noopener noreferrer">as a plugin!</a> You can read more about it in <a href="https://codefoodpixels.com/blog/2022/06/28/no-comment-2-the-webmentioning/">No Comment 2: The Webmentioning</a>.</p>

    ]]></content>
  </entry>
	
  
  <entry>
    <title>Today I was vaccinated for covid</title>
    <link href="https://codefoodpixels.com/blog/2021/03/03/today-i-was-vaccinated-for-covid/"/>
    <updated>2021-03-03T10:30:00Z</updated>
    <id>https://codefoodpixels.com/blog/2021/03/03/today-i-was-vaccinated-for-covid/</id>
    <content type="html"><![CDATA[
      <p>So today I had my first COVID vaccination. Following the examples set by <a href="https://www.brucelawson.co.uk/2021/i-got-vaccinated-for-covid/" target="_blank" rel="external noopener noreferrer">Bruce Lawson</a> and <a href="https://www.kryogenix.org/days/2021/01/28/today-i-was-vaccinated-for-covid/" target="_blank" rel="external noopener noreferrer">Stuart Langridge</a>, I felt I'd write about my experience getting the vaccine.</p>
<p><picture><source type="image/avif" srcset="https://codefoodpixels.com/static/images/yHuWqHTQ0k-250.avif 250w, https://codefoodpixels.com/static/images/yHuWqHTQ0k-600.avif 600w, https://codefoodpixels.com/static/images/yHuWqHTQ0k-800.avif 800w, https://codefoodpixels.com/static/images/yHuWqHTQ0k-1000.avif 1000w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/webp" srcset="https://codefoodpixels.com/static/images/yHuWqHTQ0k-250.webp 250w, https://codefoodpixels.com/static/images/yHuWqHTQ0k-600.webp 600w, https://codefoodpixels.com/static/images/yHuWqHTQ0k-800.webp 800w, https://codefoodpixels.com/static/images/yHuWqHTQ0k-1000.webp 1000w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/jpeg" srcset="https://codefoodpixels.com/static/images/yHuWqHTQ0k-250.jpeg 250w, https://codefoodpixels.com/static/images/yHuWqHTQ0k-600.jpeg 600w, https://codefoodpixels.com/static/images/yHuWqHTQ0k-800.jpeg 800w, https://codefoodpixels.com/static/images/yHuWqHTQ0k-1000.jpeg 1000w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/png" srcset="https://codefoodpixels.com/static/images/yHuWqHTQ0k-250.png 250w, https://codefoodpixels.com/static/images/yHuWqHTQ0k-600.png 600w, https://codefoodpixels.com/static/images/yHuWqHTQ0k-800.png 800w, https://codefoodpixels.com/static/images/yHuWqHTQ0k-1000.png 1000w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><img alt="A sticker that says 'I've had my covid vaccine' around the edges with a heart graphic in the middle" loading="lazy" decoding="async" src="https://codefoodpixels.com/static/images/yHuWqHTQ0k-250.jpeg" width="1000" height="630" /></picture></p>
<p>I fall into two of the current categories for getting the vaccination: my BMI is just over 40, so I'm part of the &quot;underlying health conditions&quot; group; and I'm an unpaid carer for my partner.</p>
<p>My partner is clinically extremely vulnerable and was vaccinated yesterday, having booked on Saturday. Because she's vulnerable, we were eager to get mine booked in as soon as possible too. I started checking every day if I could book through the <a href="https://www.nhs.uk/conditions/coronavirus-covid-19/coronavirus-vaccination/book-coronavirus-vaccination/" target="_blank" rel="external noopener noreferrer">NHS COVID vaccine booking system</a> and on Monday night, it let me book!</p>
<p>I'm staying at my partners house in Huddersfield this week in case she gets poorly from the vaccine side effects, so I booked myself to get vaccinated at the John Smith's Stadium in Huddersfield.</p>
<p>As I got close to the stadium, there were loads of temporary signs making sure people knew where the vaccine center was. Headed towards the car park and a firefighter let me know where to park and where the entrance to the vaccine center was.</p>
<p><picture><source type="image/avif" srcset="https://codefoodpixels.com/static/images/zu9oOyVCMo-250.avif 250w, https://codefoodpixels.com/static/images/zu9oOyVCMo-600.avif 600w, https://codefoodpixels.com/static/images/zu9oOyVCMo-800.avif 800w, https://codefoodpixels.com/static/images/zu9oOyVCMo-1000.avif 1000w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/webp" srcset="https://codefoodpixels.com/static/images/zu9oOyVCMo-250.webp 250w, https://codefoodpixels.com/static/images/zu9oOyVCMo-600.webp 600w, https://codefoodpixels.com/static/images/zu9oOyVCMo-800.webp 800w, https://codefoodpixels.com/static/images/zu9oOyVCMo-1000.webp 1000w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/jpeg" srcset="https://codefoodpixels.com/static/images/zu9oOyVCMo-250.jpeg 250w, https://codefoodpixels.com/static/images/zu9oOyVCMo-600.jpeg 600w, https://codefoodpixels.com/static/images/zu9oOyVCMo-800.jpeg 800w, https://codefoodpixels.com/static/images/zu9oOyVCMo-1000.jpeg 1000w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/png" srcset="https://codefoodpixels.com/static/images/zu9oOyVCMo-250.png 250w, https://codefoodpixels.com/static/images/zu9oOyVCMo-600.png 600w, https://codefoodpixels.com/static/images/zu9oOyVCMo-800.png 800w, https://codefoodpixels.com/static/images/zu9oOyVCMo-1000.png 1000w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><img alt="A sticker that says 'I've had my covid vaccine' around the edges with a heart graphic in the middle" loading="lazy" decoding="async" src="https://codefoodpixels.com/static/images/zu9oOyVCMo-250.jpeg" width="1000" height="986" /></picture></p>
<p>I walked down to the entrance gate where someone guided me to the queue to get inside. While I was in the queue, someone took my booking reference and tapped it into a tablet.</p>
<p>After queueing for about 10 minutes, I was told to go into one of the booths where there were 2 people, one sat at a computer and one getting the vaccination ready. I sat down, was asked a few questions about things like allergies, my doctor's surgery and how I got to the vaccination, and then was given the injection. The syringe was more full than the flu jab, which meant that it took a few seconds to complete.</p>
<p>They gave me an information pack with stuff about the side effects of the vaccine, a card with the date and vaccine type that I had (Oxford/Astrazenica), and I asked for a sticker.</p>
<p><picture><source type="image/avif" srcset="https://codefoodpixels.com/static/images/2lNuuXvLhJ-250.avif 250w, https://codefoodpixels.com/static/images/2lNuuXvLhJ-600.avif 600w, https://codefoodpixels.com/static/images/2lNuuXvLhJ-800.avif 800w, https://codefoodpixels.com/static/images/2lNuuXvLhJ-1000.avif 1000w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/webp" srcset="https://codefoodpixels.com/static/images/2lNuuXvLhJ-250.webp 250w, https://codefoodpixels.com/static/images/2lNuuXvLhJ-600.webp 600w, https://codefoodpixels.com/static/images/2lNuuXvLhJ-800.webp 800w, https://codefoodpixels.com/static/images/2lNuuXvLhJ-1000.webp 1000w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/jpeg" srcset="https://codefoodpixels.com/static/images/2lNuuXvLhJ-250.jpeg 250w, https://codefoodpixels.com/static/images/2lNuuXvLhJ-600.jpeg 600w, https://codefoodpixels.com/static/images/2lNuuXvLhJ-800.jpeg 800w, https://codefoodpixels.com/static/images/2lNuuXvLhJ-1000.jpeg 1000w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/png" srcset="https://codefoodpixels.com/static/images/2lNuuXvLhJ-250.png 250w, https://codefoodpixels.com/static/images/2lNuuXvLhJ-600.png 600w, https://codefoodpixels.com/static/images/2lNuuXvLhJ-800.png 800w, https://codefoodpixels.com/static/images/2lNuuXvLhJ-1000.png 1000w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><img alt="A sticker that says 'I've had my covid vaccine' around the edges with a heart graphic in the middle" loading="lazy" decoding="async" src="https://codefoodpixels.com/static/images/2lNuuXvLhJ-250.jpeg" width="1000" height="562" /></picture></p>
<p>It's been about an hour and a half since I had the vaccine, and so far the only side effect I've experienced is a sore arm.</p>
<p>Throughout the process, the staff and volunteers were fantastic: they were well organised, professional, and reassuring. The whole thing was run so smoothly and the whole operation is a testament of our public services in spite of the complete mismanagement of the entire COVID pandemic by the Tory government. I'm so relieved to have had the first dose of my vaccine, and to have my 2nd dose booked for May.</p>
<p>If you're eligible, please go get vaccinated. If you're unsure if you're currently eligible, check the <a href="https://www.nhs.uk/conditions/coronavirus-covid-19/coronavirus-vaccination/book-coronavirus-vaccination/" target="_blank" rel="external noopener noreferrer">NHS COVID vaccine booking system</a>.</p>

    ]]></content>
  </entry>
	
  
  <entry>
    <title>Now with added Eleventy!</title>
    <link href="https://codefoodpixels.com/blog/2021/02/12/now-with-added-eleventy/"/>
    <updated>2021-02-12T10:30:00Z</updated>
    <id>https://codefoodpixels.com/blog/2021/02/12/now-with-added-eleventy/</id>
    <content type="html"><![CDATA[
      <p>Ever since I gave <a href="https://11ty.dev/" target="_blank" rel="external noopener noreferrer">Eleventy</a> a go when I was building the <a href="https://leedsjs.com/" target="_blank" rel="external noopener noreferrer">LeedsJS</a> website, I've been a huge fan and advocate, even convincing some people to give it a try out of my sheer enthusiasm for it. I absolutely love the simplicity and flexibility of it, as well as things like data files. I have a whole post talking about this stuff from when I was <a href="https://codefoodpixels.com/blog/2019/11/11/building-the-new-leedsjs-website/">building the new LeedsJS website</a>.</p>
<p>I've been meaning to convert my own site over for a while, and recently took the plunge and decided to do it. As well as giving me the opportunity to dig into Eleventy without a deadline pressing me, it also gave me the chance to make some stylistic changes.</p>
<h2 id="building-the-site">Building the site</h2>
<p>My previous site was built with Hexo, which was my first foray into static site generators. Hexo is pretty prescriptive but it did the job well and gave me an easily updatable website. By comparison, Eleventy is super flexible and much more generic. There isn't a concept of a blog post in Eleventy, you just have named collections of pages with various metadata elements on them such as tags and dates. This means that you can structure your site however you want.</p>
<p>In moving over to Eleventy, I decided to rewrite a load of the HTML and CSS instead of doing a straight copy and paste. The code ended up being super similar, but I still felt it was worth it to make sure that I was doing the right thing.</p>
<h3 id="build-processing">Build processing</h3>
<p>Because you can hook into various parts of the Eleventy build process (like <code>beforeBuild</code> and after a file has been processed), I don't have to maintain various different build processes and can just have a single coherent process to get my site up and running. I've used these hooks to do some CSS processing.</p>
<p>In the <code>beforeBuild</code> event, I use <code>postcss</code> to run my CSS through <code>autoprefixer</code> to ensure that any properties that I've used are supported on older browsers.</p>
<p>I've also added a transform for HTML pages that grabs the CSS files for the whole site, runs them through <code>purgecss</code> to remove any unused code for the current page and then runs the result through <code>csso</code> to optimise it. The resulting CSS is then inlined into the page.</p>
<h3 id="images">Images</h3>
<p>Eleventy also has a plugin called <a href="https://www.11ty.dev/docs/plugins/image/" target="_blank" rel="external noopener noreferrer">Eleventy Image</a> that I've used to optimise the images. I've configured it to generate AVIF, WebP, JPEG and PNG versions of the images at widths of 1000px, 800px, 600px and 250px. It then generates a <code>picture</code> tag with these in and the most optimal image is chosen by the browser depending on what it supports and what the viewport width is.</p>
<p>I also converted a number of my images to SVGs, such as my spaceship logo. I did this by exporting the 1:1 pixel versions of the images as PNGs from the pixel art editor I use (<a href="https://www.aseprite.org/" target="_blank" rel="external noopener noreferrer">Aseprite</a>) then converted them to SVG using an excellent codepen I found called <a href="https://codepen.io/shshaw/pen/XbxvNj" target="_blank" rel="external noopener noreferrer">Pixels.svg</a>. I then ran the svgs through <a href="https://jakearchibald.github.io/svgomg/" target="_blank" rel="external noopener noreferrer">SVGOMG</a> to optimise them. While Aseprite has the option to export as SVG, it exports each pixel as a rect, which means that the SVG has a massive file size.</p>
<h3 id="using-data">Using Data</h3>
<p>I got to use one my favourite features of Eleventy when building this site: Data files. For my <a href="https://codefoodpixels.com/speaking">speaking</a> page, I wanted a list of all my talks and where I gave them. The most sensible way for me to do this (to me at least) was to have an array of talk objects. Within each talk object I have some information about the talk and an array of events I've given the talk at. This can then be used to build out the speaking page in a nice way.</p>
<h2 id="design-changes">Design changes</h2>
<p>While rebuilding the site, I decided to make a few design changes. I still love the pixel art space theme and colour scheme, but felt I could make some of the bits more pixel-art like and make some things stand out more.</p>
<h3 id="making-a-hero-out-of-the-homepage">Making a hero out of the homepage</h3>
<p>My old website just had the blog listing on the homepage and the same header as the rest of the website. I wanted to make the site a little bit more about me, so I moved the content of the about page to the homepage, and moved the blog to live under the <code>/blog/</code> path.</p>
<p>I then decided to differentiate the header on the homepage a bit and changed it to take up almost the whole screen. I then added the asteroids from the hidden game I have on the site, animated the spaceship to move around a little more, and added some laser shots.</p>
<p>I'm really happy with how the homepage has turned out and think it gives my site a bit more personality!</p>
<h3 id="navigation">Navigation</h3>
<p>The navigation on the old site stood out alright, but it never quite looked how I wanted. On the new site, I've made it a complete red bar, with the red buttons surrounded by a black border.</p>
<p>I've changed the 3D effect on the buttons too. Previously they were using <code>border-style: outset</code> but I used CSS gradients to make the borders look a bit more like a pixel art button. I also took the oppotunity to add a rollover effect to the buttons, to make them look like they're pressed. It sort of reminds me of the rollover effects you'd get on websites in the 90s!</p>
<h3 id="author-bar">Author bar</h3>
<p>I've added an author bar to the bottom of my blog posts with a photo of me, my bio and some links to follow me on twitter and subscribe to my RSS feed. I feel that this makes the page feel a little more complete and makes it feel less like the page is abruptly ending.</p>

    ]]></content>
  </entry>
	
  
  <entry>
    <title>Do I need bunting today?</title>
    <link href="https://codefoodpixels.com/blog/2020/05/25/do-i-need-bunting-today/"/>
    <updated>2020-05-25T10:30:00Z</updated>
    <id>https://codefoodpixels.com/blog/2020/05/25/do-i-need-bunting-today/</id>
    <content type="html"><![CDATA[
      <p>A couple of weeks ago an idea popped into my head, I built it in a few hours and today, as it's a bank holiday, I'm launching it into the world!</p>
<p>Introducing <a href="https://doineedbuntingtoday.com/" target="_blank" rel="external noopener noreferrer">Do I need bunting today?</a>!</p>
<h2 id="what-is-it">What is it?</h2>
<p><a href="https://doineedbuntingtoday.com/" target="_blank" rel="external noopener noreferrer">Do I need bunting today?</a> is a site that tells you whether bunting is appropriate, based on if it's a bank holiday in England, Wales, Scotland or Northern Ireland.</p>
<p>That's it, that's the site.</p>
<h2 id="ok-but-why">Ok, but why?</h2>
<p>Because I could 🤷‍♂️</p>
<p>The GOV.UK website has a list of bank holidays available at <a href="https://www.gov.uk/bank-holidays" target="_blank" rel="external noopener noreferrer">https://www.gov.uk/bank-holidays</a>. This list is well presented, easy to read and I frequently use it to see when bank holidays are because I forget.</p>
<p>But a while ago, I learned that if you add <code>.json</code> to the end of that URL, you get a JSON represenation of the bank holidays, which includes a true/false value for whether bunting is appropriate.</p>
<p>So I decided to make a site using <a href="https://11ty.dev/" target="_blank" rel="external noopener noreferrer">Eleventy</a> with JavaScript data files. The site grabs the data from <a href="https://www.gov.uk/bank-holidays.json" target="_blank" rel="external noopener noreferrer">https://www.gov.uk/bank-holidays.json</a>, manipulates it into a format that I can use to show information to the user and then it displays it in a massive, easy to read format for the user.</p>
<p>It's hosted on Netlify and uses a GitHub Action to rebuild the site every day using a Netlify Build Hook.</p>

    ]]></content>
  </entry>
	
  
  <entry>
    <title>CSS Naked Day 2020</title>
    <link href="https://codefoodpixels.com/blog/2020/04/09/css-naked-day-2020/"/>
    <updated>2020-04-09T10:30:00Z</updated>
    <id>https://codefoodpixels.com/blog/2020/04/09/css-naked-day-2020/</id>
    <content type="html"><![CDATA[
      <p>Today is <a href="https://css-naked-day.github.io/" target="_blank" rel="external noopener noreferrer">CSS naked day 2020</a>! Inspired by <a href="https://meyerweb.com/" target="_blank" rel="external noopener noreferrer">Eric Meyer</a>, <a href="https://laurakalbag.com/css-naked-day-2020/" target="_blank" rel="external noopener noreferrer">Laura Kalbag</a> and <a href="https://hankchizljaw.com/wrote/css-naked-day-2020/" target="_blank" rel="external noopener noreferrer">Andy Bell</a>, I've removed the CSS from my site for today.</p>
<p>The process for me was super easy! Because my HTML was already well structured and I had decent default sizes for my images, I just had to remove the links to the CSS files and it worked!</p>

    ]]></content>
  </entry>
	
  
  <entry>
    <title>The Viewbuilder Pattern</title>
    <link href="https://codefoodpixels.com/blog/2020/02/01/the-viewbuilder-pattern/"/>
    <updated>2020-02-01T10:30:00Z</updated>
    <id>https://codefoodpixels.com/blog/2020/02/01/the-viewbuilder-pattern/</id>
    <content type="html"><![CDATA[
      <p>Many companies have internal APIs that provide their data, but scaling these can be tricky and expensive. There are also cases where you're using a rate-limited 3rd party API that you need to use to provide data to the frontend of your website.</p>
<p>While I was working at Sky Betting and Gaming, I was introduced to a pattern that they use called a viewbuilder. I find it to be a really interesting and useful idea, and we used it heavily in my time there.</p>
<h2 id="what-is-a-viewbuilder">What is a viewbuilder?</h2>
<p>At its most simple, a viewbuilder is a process that runs a task on a set interval to gather, manipulate and store data for later use.</p>
<p>The interval you set can depend how often the data will update and how fresh you want it to be. Most of our instances ran at a 1 second interval, but some ran at other intervals such as 5 and 10 seconds.</p>
<h2 id="why-use-a-viewbuilder">Why use a viewbuilder?</h2>
<p>While you could gather and return this data as and when it is requested by the frontend, this can be slow and can introduce scaling and caching issues.</p>
<p>By using a viewbuilder we only gather this data once within a set interval, reducing the load on any APIs used and reducing the work done during the call from the frontend.</p>
<p>This may sound just like caching, but there are 2 key differences. The first is that caching is usually done on the first request. In cases of heavy traffic sites, this can result in a cache stampede and potentially bringing your site down. The second difference is that this is a transformed cache, so there is no extra processing necessary when making the request.</p>
<p>The downside of the viewbuilder approach is that the data is only as fresh as the frequency of the viewbuilder. This may be a problem for some use cases, but it was acceptable for us.</p>
<h2 id="anatomy-of-a-viewbuilder">Anatomy of a viewbuilder</h2>
<p>When we first start the viewbuilder process, we initialise things like logging and any necessary connections to data stores. We then set the interval to run the viewbuilder task.</p>
<p>The task is usually broken down into 3 stages:</p>
<h3 id="read">Read</h3>
<p>At the read stage, we'd gather all the data we need for that particular process. This may be calls to internal APIs, calls to 3rd party APIs or even direct database calls.</p>
<p>We would frequently gather data from multiple sources at this stage, and would occasionally have cases where the data from one source would be used to gather data from another.</p>
<h3 id="transform">Transform</h3>
<p>During the transform stage, the data from the read stage would be combined and manipulated to produce the desired output.</p>
<h3 id="write">Write</h3>
<p>At the write stage, we would write the document into MongoDB for later use. We would also add a timestamp so that we can see how fresh the data was when debugging.</p>

    ]]></content>
  </entry>
	
  
  <entry>
    <title>Connecting my Christmas jumper to the Internet</title>
    <link href="https://codefoodpixels.com/blog/2019/12/13/connecting-my-christmas-jumper-to-the-internet/"/>
    <updated>2019-12-13T10:30:00Z</updated>
    <id>https://codefoodpixels.com/blog/2019/12/13/connecting-my-christmas-jumper-to-the-internet/</id>
    <content type="html"><![CDATA[
      <p>Some of you will be aware that while I'm not necessarily great at it, I enjoy playing around with hardware. One of the talks that I've been fortunate enough to travel the world to give is about using JavaScript to do bits of home automation, including automating my curtains and making my own thermostat.</p>
<p>So it may come as no real surprise that when I heard there was going to be a Christmas jumper competition at work last year, I decided add LEDs to my jumper and connect it to the Internet.</p>
<h2 id="step-1-the-jumper">Step 1 - The jumper</h2>
<p>I didn't want to start with an off-the-shelf Christmas jumper, that feels a little like cheating. Instead, I decided to get a plain jumper and add my own stuff to it. I went with a dark red jumper with a chunky knit from Primark that cost £8.</p>
<p><picture><source type="image/avif" srcset="https://codefoodpixels.com/static/images/gp1LVTQ7Jr-250.avif 250w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/webp" srcset="https://codefoodpixels.com/static/images/gp1LVTQ7Jr-250.webp 250w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/png" srcset="https://codefoodpixels.com/static/images/gp1LVTQ7Jr-250.png 250w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><img alt="A dark red jumper with chunky knit" loading="lazy" decoding="async" src="https://codefoodpixels.com/static/images/gp1LVTQ7Jr-250.jpeg" width="250" height="300" /></picture></p>
<p>I then started thinking about what I was going to do for the design. The most obvious idea to me was a Christmas tree, as I could scatter LEDs over it and light it up like an actual Christmas tree.</p>
<p>As I'm no good at sewing and don't possess a sewing machine, I asked <a href="https://www.instagram.com/cerisebonaccorsi/" target="_blank" rel="external noopener noreferrer">my sister Cerise</a> for her help at this stage. I cut a Christmas tree shape out of felt, and she sewed it onto the jumper for me.</p>
<p>Once that was done, I got some sparkly pipe cleaners and used a hot glue gun to attach them to the felt in a tinsel-like design. At this stage I also marked and poked holes in the felt for where the lights would come out.</p>
<p><picture><source type="image/avif" srcset="https://codefoodpixels.com/static/images/ZBx8rYqXvu-250.avif 250w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/webp" srcset="https://codefoodpixels.com/static/images/ZBx8rYqXvu-250.webp 250w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/png" srcset="https://codefoodpixels.com/static/images/ZBx8rYqXvu-250.png 250w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><img alt="A dark red jumper with chunky knit and a christmas tree design" loading="lazy" decoding="async" src="https://codefoodpixels.com/static/images/ZBx8rYqXvu-250.jpeg" width="250" height="300" /></picture></p>
<h2 id="step-2-the-lights">Step 2 - The lights</h2>
<p>For the lights I started with a set of battery powered Christmas lights that I bought for £1. I chose these because the fact that they were battery powered meant I could wire them up to a microcontroller and control the power to them to make them flash or fade.</p>
<p><picture><source type="image/avif" srcset="https://codefoodpixels.com/static/images/vCq2TvaX2v-250.avif 250w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/webp" srcset="https://codefoodpixels.com/static/images/vCq2TvaX2v-250.webp 250w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/png" srcset="https://codefoodpixels.com/static/images/vCq2TvaX2v-250.png 250w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><img alt="An ESP8266 board" loading="lazy" decoding="async" src="https://codefoodpixels.com/static/images/vCq2TvaX2v-250.jpeg" width="250" height="300" /></picture></p>
<p>One of the boards that I love to use when I build things is the ESP8266 as it's cheap, has wifi and, by flashing <a href="https://www.espruino.com/" target="_blank" rel="external noopener noreferrer">Espruino</a> on it, can be programmed with JavaScript.</p>
<p><picture><source type="image/avif" srcset="https://codefoodpixels.com/static/images/o4sZmpHuad-250.avif 250w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/webp" srcset="https://codefoodpixels.com/static/images/o4sZmpHuad-250.webp 250w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/png" srcset="https://codefoodpixels.com/static/images/o4sZmpHuad-250.png 250w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><img alt="An ESP8266 board" loading="lazy" decoding="async" src="https://codefoodpixels.com/static/images/o4sZmpHuad-250.jpeg" width="250" height="250" /></picture></p>
<p>The Christmas lights were originally powered by 2 AA batteries in series, which means that 3 volts of power were being delivered to the lights. The ESP8266 has a 3.3v pin, which I used to power the lights instead.</p>
<p>To be able to control the lights, I used a transistor connected between the lights and the ground pin, with the control pin connected to one of the GPIO pins on the ESP8266. This meant that I could control the resistance that the transistor was providing, and could use it to dim or flash the lights. You can see a wiring diagram below.</p>
<p><picture><source type="image/avif" srcset="https://codefoodpixels.com/static/images/rNjyDyFOi8-250.avif 250w, https://codefoodpixels.com/static/images/rNjyDyFOi8-600.avif 600w" sizes="(min-width: 664px) 600px, 250px" /><source type="image/webp" srcset="https://codefoodpixels.com/static/images/rNjyDyFOi8-250.webp 250w, https://codefoodpixels.com/static/images/rNjyDyFOi8-600.webp 600w" sizes="(min-width: 664px) 600px, 250px" /><source type="image/jpeg" srcset="https://codefoodpixels.com/static/images/rNjyDyFOi8-250.jpeg 250w, https://codefoodpixels.com/static/images/rNjyDyFOi8-600.jpeg 600w" sizes="(min-width: 664px) 600px, 250px" /><source type="image/png" srcset="https://codefoodpixels.com/static/images/rNjyDyFOi8-250.png 250w, https://codefoodpixels.com/static/images/rNjyDyFOi8-600.png 600w" sizes="(min-width: 664px) 600px, 250px" /><img alt="A wiring diagram for connecting the christmas lights to an esp8266" loading="lazy" decoding="async" src="https://codefoodpixels.com/static/images/rNjyDyFOi8-250.jpeg" width="600" height="340" /></picture></p>
<h2 id="step-3-putting-it-together">Step 3 - Putting it together</h2>
<p>The last step of the hardware side was putting the lights in the jumper. As I'd already created holes in the felt for the lights, I just needed to get the lights through the jumper itself.</p>
<p>Part of the reason that I chose the chunky knit jumper was so that I can push the lights through the gaps in the knit and then push it through the holes in the felt.</p>
<p><picture><source type="image/avif" srcset="https://codefoodpixels.com/static/images/735RtG6mFt-250.avif 250w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/webp" srcset="https://codefoodpixels.com/static/images/735RtG6mFt-250.webp 250w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/png" srcset="https://codefoodpixels.com/static/images/735RtG6mFt-250.png 250w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><img alt="The wiring on the inside of the jumper" loading="lazy" decoding="async" src="https://codefoodpixels.com/static/images/735RtG6mFt-250.jpeg" width="250" height="300" /></picture></p>
<p>One issue that I ran into was the lights shifting and occasionally falling behind the felt. This was pretty annoying, but fix was pretty easy. I added a dab of hot glue to each light to hold it onto the felt.</p>
<p><picture><source type="image/avif" srcset="https://codefoodpixels.com/static/images/ZwaDZwJwFT-250.avif 250w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/webp" srcset="https://codefoodpixels.com/static/images/ZwaDZwJwFT-250.webp 250w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/png" srcset="https://codefoodpixels.com/static/images/ZwaDZwJwFT-250.png 250w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><img alt="The jumper with the lights on" loading="lazy" decoding="async" src="https://codefoodpixels.com/static/images/ZwaDZwJwFT-250.jpeg" width="250" height="300" /></picture></p>
<h2 id="step-4-controlling-the-lights">Step 4 - Controlling the lights</h2>
<p>An Internet connected Christmas jumper isn't complete without being connected to and controlled from the Internet.</p>
<p>As mentioned earlier, I'm using the ESP8266. It has wifi which means I can do some communication. When I'd attended JSHeroes in 2018, <a href="https://twitter.com/stephaniecodes" target="_blank" rel="external noopener noreferrer">Stephanie Nemeth</a> talked about connecting her LED projects to the Internet. One of the key takwaways for me was that sockets aren't necessarily the greatest way to connect, and that MQTT may be better. <a href="https://www.youtube.com/watch?v=VTxsAiznxt4" target="_blank" rel="external noopener noreferrer">Stephanie's talk is up on YouTube</a>.</p>
<p>With this in mind, I set about building the software with MQTT as the communication method. As part of Espruino there's a MQTT module, which makes the hardware side easy. On the web side I chose to use <a href="https://github.com/mqttjs/MQTT.js" target="_blank" rel="external noopener noreferrer">MQTT.js</a>, and I chose <a href="https://shiftr.io/" target="_blank" rel="external noopener noreferrer">shiftr.io</a> as my MQTT broker.</p>
<p><picture><source type="image/avif" srcset="https://codefoodpixels.com/static/images/OsEs24gSzX-250.avif 250w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/webp" srcset="https://codefoodpixels.com/static/images/OsEs24gSzX-250.webp 250w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/png" srcset="https://codefoodpixels.com/static/images/OsEs24gSzX-250.png 250w" sizes="(min-width: 73.25rem) 62.5rem, (min-width: 54rem) 50rem, (min-width: 41.5rem) 37.5rem, 15.625rem" /><img alt="A webapp for controlling the jumper lights" loading="lazy" decoding="async" src="https://codefoodpixels.com/static/images/OsEs24gSzX-250.jpeg" width="250" height="322" /></picture></p>
<p>I'm not going to go super in-depth on the web app, but I used plain JavaScript, HTML and CSS to build a form for the various ways to control the lights. With the fade and flash options, the user has the choice of controlling how long the lights stay on and off for. As I use MQTT, I don't need any server-side code for this app which means that I can deploy it as a static site on <a href="https://www.netlify.com/" target="_blank" rel="external noopener noreferrer">Netlify</a>.</p>
<h2 id="the-final-product">The final product</h2>
<p>I'm super happy with the final product. It's great fun to have people play with it, even from across the world!</p>
<p>You can find all of the code in the <a href="https://github.com/CodeFoodPixels/jumper-lights" target="_blank" rel="external noopener noreferrer">jumper-lights repo</a> on my github, and below is a video of my friend Beth controlling it!</p>
<p><video controls="" src="https://codefoodpixels.com/static/videos/jumper-flashing.mp4" width="320" autoplay="true" loop="true" muted="true"></video></p>

    ]]></content>
  </entry>
	
  
  <entry>
    <title>Building the new LeedsJS website</title>
    <link href="https://codefoodpixels.com/blog/2019/11/11/building-the-new-leedsjs-website/"/>
    <updated>2019-11-11T10:30:00Z</updated>
    <id>https://codefoodpixels.com/blog/2019/11/11/building-the-new-leedsjs-website/</id>
    <content type="html"><![CDATA[
      <p>At the end of 2018, I announced that I was setting a 6 month deadline for LeedsJS to move away from Meetup. At the time they charged $90 for 6 months as an organiser, which I was paying out of my own pocket. At the time of writing, this has been raised to $98.94.</p>
<p><picture><source type="image/avif" srcset="https://codefoodpixels.com/static/images/x_pN1Szx5f-250.avif 250w, https://codefoodpixels.com/static/images/x_pN1Szx5f-600.avif 600w, https://codefoodpixels.com/static/images/x_pN1Szx5f-800.avif 800w, https://codefoodpixels.com/static/images/x_pN1Szx5f-1000.avif 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/webp" srcset="https://codefoodpixels.com/static/images/x_pN1Szx5f-250.webp 250w, https://codefoodpixels.com/static/images/x_pN1Szx5f-600.webp 600w, https://codefoodpixels.com/static/images/x_pN1Szx5f-800.webp 800w, https://codefoodpixels.com/static/images/x_pN1Szx5f-1000.webp 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/jpeg" srcset="https://codefoodpixels.com/static/images/x_pN1Szx5f-250.jpeg 250w, https://codefoodpixels.com/static/images/x_pN1Szx5f-600.jpeg 600w, https://codefoodpixels.com/static/images/x_pN1Szx5f-800.jpeg 800w, https://codefoodpixels.com/static/images/x_pN1Szx5f-1000.jpeg 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/png" srcset="https://codefoodpixels.com/static/images/x_pN1Szx5f-250.png 250w, https://codefoodpixels.com/static/images/x_pN1Szx5f-600.png 600w, https://codefoodpixels.com/static/images/x_pN1Szx5f-800.png 800w, https://codefoodpixels.com/static/images/x_pN1Szx5f-1000.png 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><img alt="I've just made the last payment I'm going to make to @Meetup. They charge $90 for 6 months. I've been paying this myself ever since I took @leedsjs over. Charging community run groups this amount of money is bizarre to me, and I'm not going to pay it anymore." loading="lazy" decoding="async" src="https://codefoodpixels.com/static/images/x_pN1Szx5f-250.jpeg" width="1000" height="603" /></picture></p>
<p>Many community groups try to keep their costs as low as possible, and those that get sponsorship usually get it on a month-to-month basis and attibute it directly to something. This makes paying for a 6 month subscription to a service difficult, and organisers end up paying themselves.</p>
<p>I also felt that the service that Meetup was providing for the cost wasn't great. Over the past year or two, they've updated the site and have taken away a lot of flexibility.</p>
<h2 id="requirements">Requirements</h2>
<p>There were 3 key features that we used on Meetup that we needed to replace, as well as a couple of extra features that I wanted.</p>
<ul>
<li>
<p><strong>Event page:</strong> We needed a page for each event detailing the talks, the venue, the date and time, and the sponsors for that event.</p>
</li>
<li>
<p><strong>RSVPs:</strong> We need to have an idea of how many people will be attending, and to be able to set a maximum number of attendees.</p>
</li>
<li>
<p><strong>Mailing list/email notifications:</strong> We want to be able to contact our members with long form event notifications, as well as any other messages that we want to share with them such as conference discounts.</p>
</li>
<li>
<p><strong>Speaker pages:</strong> I wanted to be able to have profile pages for speakers so that people can see any previous talks that they've given and find any links that the speakers wanteed to share.</p>
</li>
<li>
<p><strong>Talk pages:</strong> I wanted to have pages for the talks so that after the talk we can share the video on there with all the other details of the talk.</p>
</li>
</ul>
<h2 id="external-services">External services</h2>
<p>Instead of implementing them myself, I decided to use external services for RSVPs and emails. These are usually complex systems with a lot of moving parts, and I felt it was much better to rely on tried and tested systems.</p>
<p>For RSVPs I decided to use <a href="https://ti.to/" target="_blank" rel="external noopener noreferrer">Tito</a>. Tito is free for free events and has a pretty streamlined experience when getting a ticket. It also has a very good admin experience.</p>
<p>For email I chose <a href="https://mailchimp.com/" target="_blank" rel="external noopener noreferrer">Mailchimp</a>. Mailchimp has a pretty good free tier, and their limits surpass anything that we need.</p>
<h2 id="building-the-website">Building the website</h2>
<p>In the past couple of years, I've rediscovered my love for static sites, and static site generators have played a massive part of that. I use Hexo to build my own website, but I'd heard great things about another static site generator called <a href="https://www.11ty.io/" target="_blank" rel="external noopener noreferrer">Eleventy</a> and decided to give that a go.</p>
<p>One of the great features of Eleventy is the ability to have <a href="https://www.11ty.io/docs/data-global/" target="_blank" rel="external noopener noreferrer">global data files</a>. This allows you to define your data in JavaScript or JSON files, and have it available to use in your pages. I've used this heavily with the LeedsJS website, as it means we can split the data and use it in various forms.</p>
<h3 id="data">Data</h3>
<p>I've broken the data down into 4 sections, which are linked in various ways</p>
<h4 id="speaker">Speaker</h4>
<p>This contains information about a speaker. This is the file for me on the new site:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span><br />  <span class="token property">"id"</span><span class="token operator">:</span> <span class="token string">"luke-bonaccorsi"</span><span class="token punctuation">,</span><br />  <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"Luke Bonaccorsi"</span><span class="token punctuation">,</span><br />  <span class="token property">"bio"</span><span class="token operator">:</span> <span class="token string">"..."</span><span class="token punctuation">,</span><br />  <span class="token property">"picture"</span><span class="token operator">:</span> <span class="token string">"luke-bonaccorsi.jpg"</span><span class="token punctuation">,</span><br />  <span class="token property">"twitter"</span><span class="token operator">:</span> <span class="token string">"CodeFoodPixels"</span><span class="token punctuation">,</span><br />  <span class="token property">"links"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br />    <span class="token property">"Website"</span><span class="token operator">:</span> <span class="token string">"https://codefoodpixels.com"</span><span class="token punctuation">,</span><br />    <span class="token property">"GitHub"</span><span class="token operator">:</span> <span class="token string">"https://github.com/codefoodpixels"</span><br />  <span class="token punctuation">}</span><br /><span class="token punctuation">}</span></code></pre>
<h4 id="talk">Talk</h4>
<p>This contains information about a talk. It links to a speaker using the speaker's ID.</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span><br />  <span class="token property">"id"</span><span class="token operator">:</span> <span class="token string">"coding-is-serious-business"</span><span class="token punctuation">,</span><br />  <span class="token property">"title"</span><span class="token operator">:</span> <span class="token string">"Coding Is Serious Business"</span><span class="token punctuation">,</span><br />  <span class="token property">"speaker"</span><span class="token operator">:</span> <span class="token string">"luke-bonaccorsi"</span><span class="token punctuation">,</span><br />  <span class="token property">"abstract"</span><span class="token operator">:</span> <span class="token string">"..."</span><span class="token punctuation">,</span><br />  <span class="token property">"date"</span><span class="token operator">:</span> <span class="token string">"2019-02-27"</span><span class="token punctuation">,</span><br />  <span class="token property">"youtube_video_id"</span><span class="token operator">:</span> <span class="token string">"CWiiKljO7D0"</span><br /><span class="token punctuation">}</span></code></pre>
<h4 id="event">Event</h4>
<p>This contains the information for an event. This includes the talks, sponsors and the dates of various stages.</p>
<p>The site uses the <code>announce_date</code> property to decide whether to show the event on the site, and it uses the <code>ticket_date</code> property to decide if it sould show the ticket button.</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span><br />  <span class="token property">"id"</span><span class="token operator">:</span> <span class="token string">"2019-02-27"</span><span class="token punctuation">,</span><br />  <span class="token property">"title"</span><span class="token operator">:</span> <span class="token string">"February - Luke Bonaccorsi &amp; Wade Penistone"</span><span class="token punctuation">,</span><br />  <span class="token property">"blurb"</span><span class="token operator">:</span> <span class="token string">"..."</span><span class="token punctuation">,</span><br />  <span class="token property">"talks"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"coding-is-serious-business"</span><span class="token punctuation">,</span> <span class="token string">"mindstack"</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br />  <span class="token property">"sponsors"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br />    <span class="token string">"sky-betting-and-gaming"</span><span class="token punctuation">,</span><br />    <span class="token string">"bruntwood"</span><span class="token punctuation">,</span><br />    <span class="token string">"starlight-software"</span><span class="token punctuation">,</span><br />    <span class="token string">"jetbrains"</span><span class="token punctuation">,</span><br />    <span class="token string">"frontendne"</span><br />  <span class="token punctuation">]</span><span class="token punctuation">,</span><br />  <span class="token property">"date"</span><span class="token operator">:</span> <span class="token string">"2019-02-27"</span><span class="token punctuation">,</span><br />  <span class="token property">"start_time"</span><span class="token operator">:</span> <span class="token string">"18:30"</span><span class="token punctuation">,</span><br />  <span class="token property">"end_time"</span><span class="token operator">:</span> <span class="token string">"20:30"</span><span class="token punctuation">,</span><br />  <span class="token property">"ticket_date"</span><span class="token operator">:</span> <span class="token string">"2019-02-20"</span><span class="token punctuation">,</span><br />  <span class="token property">"announce_date"</span><span class="token operator">:</span> <span class="token string">"2019-02-01"</span><br /><span class="token punctuation">}</span></code></pre>
<h4 id="sponsor">Sponsor</h4>
<p>This contains the information about a sponsor so we can display it wherever we need to.</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span><br />  <span class="token property">"id"</span><span class="token operator">:</span> <span class="token string">"sky-betting-and-gaming"</span><span class="token punctuation">,</span><br />  <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"Sky Betting &amp; Gaming"</span><span class="token punctuation">,</span><br />  <span class="token property">"url"</span><span class="token operator">:</span> <span class="token string">"https://www.skybetcareers.com/"</span><span class="token punctuation">,</span><br />  <span class="token property">"logo"</span><span class="token operator">:</span> <span class="token string">"sky-betting-and-gaming.png"</span><span class="token punctuation">,</span><br />  <span class="token property">"twitter"</span><span class="token operator">:</span> <span class="token string">"SkyBetCareers"</span><br /><span class="token punctuation">}</span></code></pre>
<h3 id="pages">Pages</h3>
<p>After deciding how to split the data, I had to build the pages and decide how to structure the site. This fell into a similar pattern as the data.</p>
<h4 id="homepage">Homepage</h4>
<p>For the homepage, I wanted the next event to be the main focus. The main purpose of the group is to hold these events, and I feel it's the primary reason that people visit our website.</p>
<p>I didn't want to overload the homepage with all of the information from the event, so I decided to limit it to the title, date, time, event blurb, talk titles and speakers, and then the buttons for more details and tickets. I feel that this gives a pretty good overview of the event and people can click through to get more details if they want.</p>
<p>I also added some information about the group itself and the venue for our events.</p>
<h4 id="events">Events</h4>
<p>Each event gets a page on our new site. This page contains all the details about that event, including the talks, sponsors and ticket information, as well as the date, time and blurb for the event.</p>
<p>When it's the current event, this page is linked to from the homepage and any communications such as emails and tweets. When the event is over, the page still has all the details of the event and also embeds the videos for the talks.</p>
<p>We also have a listing of all of the events (starting from our first event of 2019) with the most recent at the top.</p>
<h4 id="speakers">Speakers</h4>
<p>Every speaker has their own profile page on the site, which includes their biography, an image, any links that they want and links to all the talks that they've given.</p>
<p>All the speakers are also listed on a directory page in alphabetical order.</p>
<h4 id="talks">Talks</h4>
<p>Each talk has a page that includes the title, the date the talk was given, a link to the speaker's profile page and the abstract for the talk. After the event, the youtube video for the talk will be embedded too.</p>
<p>There's also a listing page with all the talks in alphabetical order.</p>
<h4 id="feedback">Feedback</h4>
<p>This was an addition that I made after we'd launched the website and sucessfully used it for an event. The website generates a feedback form for attendees to submit feedback about the event and the talks.</p>
<h3 id="performance">Performance</h3>
<p>I wanted the site to be quick, so I took a few steps to help this.</p>
<p>The easiest step was ensuring that we don't serve any huge images. As part of the build we have a script that runs to resize them all down to a maximum of 300 pixels in either width or height. This is the largest that an image will be displayed on the website.</p>
<p>Another step that I took was to leverage caching. While this doesn't have an impact on the initial load, it does on subsequent loads. To do this, I use a service worker to store assets in a cache, and the browser will look in the cache before then trying the network.</p>
<p>After reading the <a href="https://2019.jsconf.eu/news/how-we-built-the-fastest-conference-website-in-the-world/" target="_blank" rel="external noopener noreferrer">&quot;How we built the fastest conference website in the world&quot; post from the JSConf EU blog</a>, I decided to follow the same process of building a stylesheet of styles for a particular page and then inlining it into the page. This improved our rendering time significantly.</p>
<p>Finally, I tried to use as little JavaScript on the website itself as possible. While I love JavaScript and we're a JavaScript group, it'd be irresponsible to add a load of page weight with unnecessary JavaScript.</p>
<h2 id="automation">Automation</h2>
<p>I'm lazy. I don't want to have to do a load of little tasks every time we announce a new event, or when tickets become available, or when the event, or when the event is finished...</p>
<p>So I set out with the goal of being able to automate as much of the site as possible. Initially I thought this was going to be something I ended up doing after we'd used the site for a bit, but the way that I'd structured the data really helped here and meant that I was able to do it fairly easily.</p>
<h3 id="time-based-content">Time based content</h3>
<p>Because some of the stuff such as event announcements and ticket releases are driven by time, I want to only show those things when they should be available.</p>
<p>In the data, I store dates for the event announcements and ticket releases which I then check against when generating the site. If it is currently on or past the date, then it shows the content.</p>
<p>But this still means that I have to build the site every day, and I don't want to do that manually. Thankfully I can combine <a href="https://docs.netlify.com/configure-builds/build-hooks/" target="_blank" rel="external noopener noreferrer">Netlify build hooks</a> with a scheduled serverless function to rebuild the site daily.</p>
<h3 id="next-eventjson">next-event.json</h3>
<p>Because all the data is linked together, I can generate a JSON file with a load of data about the next event such as the title, talk titles, speaker information and dates like the announcement date and ticket release date. This file then sits on the site so that it can be used for other tasks.</p>
<h3 id="emails">Emails</h3>
<p>As I mentioned earlier, we're using Mailchimp for our emails. One of the reasons that I didn't mention for choosing them is that they have an API that we can use to create and send email campaigns. Additionally, you can provide your content as HTML through the API to go into a template.</p>
<p>As part of the site build process, I generate the html for the various emails that we send. I then have a scheduled serverless function that runs 15 minutes after the rebuild of the website which grabs the next-event.json, checks if it should be sending any emails and if so, grabs the relevant HTML, builds the campaign and sends it.</p>
<h3 id="tweets">Tweets</h3>
<p>Besides email, our other main way to communicate about the event is via Twitter and we do this at about the same cadence as our emails.</p>
<p>I have a scheduled serverless function that I use to post through Twitter's API. The script grabs the next-event.json, determines if a tweet should be posted that day. If so, it then determines the content for the tweet and then posts it through the API.</p>
<h3 id="tickets">Tickets</h3>
<p>We're using Tito for our tickets, and Tito also has an API that we can use as part of the automation.</p>
<p>In a scheduled serverless function, we grab the next-event.json and check if it's announcement day. If so, then we create the tickets through the Tito API and set the tickets to be available on the ticket release day.</p>
<h3 id="streaming-overlay">Streaming overlay</h3>
<p>When possible, we stream our talks on YouTube. This means that people who can't make the event can watch the talks as they happen, as well as getting a recording for later. As part of this, we want to put some information about the speaker and the talk on the stream, as well as the video feeds.</p>
<p>As we have all the data we need in the data files, we can generate a HTML page with a fixed dimension and then put the information about the speaker and talk into the right areas.</p>
<p>When it comes time to stream, we can drop this page into OBS with the browser plugin and then have this as part of the stream.</p>
<h3 id="intro-slides">Intro slides</h3>
<p>Another thing that gets generated as part of the website is the slides for the introductions at the start of the event. While some of the stuff on the slides is static (such as the code of conduct, social media links and mailing list info), other stuff is based on data from the event. When generating the site, we pull this information from the event, talk and speaker data and render it into a HTML page that uses CSS scroll-snap to create slides.</p>

    ]]></content>
  </entry>
	
  
  <entry>
    <title>Get involved in your local community groups: Why I love LeedsJS</title>
    <link href="https://codefoodpixels.com/blog/2019/09/19/get-involved-in-your-local-community-groups-why-i-love-leedsjs/"/>
    <updated>2019-09-19T10:30:00Z</updated>
    <id>https://codefoodpixels.com/blog/2019/09/19/get-involved-in-your-local-community-groups-why-i-love-leedsjs/</id>
    <content type="html"><![CDATA[
      <p>I first got involved in <a href="https://leedsjs.com/" target="_blank" rel="external noopener noreferrer">LeedsJS</a> in 2015, and then took over as the main organiser in 2016. Since then, we've grown from around 15 people per event to about 60 per event.</p>
<p>The biggest drive for LeedsJS is sharing knowledge so that our attendees can become better developers. In recent years I've expanded the scope of this by adding another talk spot where we try and get talks on topics such as mental health, testing and inclusivity. These are topics that make people better teammates, and this fits well with the goal of helping people become better developers.</p>
<p>As a group, we welcome people with all levels of experience, from those that have been writing JavaScript since it first came around, to those who have no real experience writing it at all. This mix means that some of the junior developers who attend have had a real boost from being part of the community, through speaking to more experienced developers and asking questions.</p>
<p>We also provide a platform for people who want to share their knowledge and try public speaking. The majority of our speakers are local, and over the past couple of years we've had a number of first-time speakers who have gone on to speak all over the world (myself included).</p>
<p>Another thing that I've been pushing is ensuring that we make the group inclusive. I've made some changes over the past few years with the aim of reducing the barriers for people to attend our events, such as <a href="https://codefoodpixels.com/blog/2018/05/06/Why-LeedsJS-Doesn-t-Have-Q-A-Anymore/">removing Q&amp;A</a>, <a href="https://codefoodpixels.com/blog/2019/05/15/your-event-probably-shouldn-t-be-in-a-pub/">moving our events to a alcohol free venue</a> and accounting for dietary restrictions when picking the food.</p>
<p>Independent community groups are hugely important to the tech scene in any city, as they bring people together from all backgrounds with the sole aim of making everyone better. In Leeds we're very lucky to have a number of really fantastic grassroots community groups run by organisers who volunteer their time, and I'm proud to be part of that group.</p>

    ]]></content>
  </entry>
	
  
  <entry>
    <title>Your Event Probably Shouldn&#39;t Be In A Pub</title>
    <link href="https://codefoodpixels.com/blog/2019/05/15/your-event-probably-shouldnt-be-in-a-pub/"/>
    <updated>2019-05-15T10:30:00Z</updated>
    <id>https://codefoodpixels.com/blog/2019/05/15/your-event-probably-shouldnt-be-in-a-pub/</id>
    <content type="html"><![CDATA[
      <p>When we were looking for a new venue for <a href="https://leedsjs.com/" target="_blank" rel="external noopener noreferrer">LeedsJS</a> a few years ago, one of the criteria I set was &quot;not a pub&quot;. Initially this seems like an arbitrary decision, but I had a number of reasons for doing so.</p>
<p>At the time, we were based in a pub but our the diversity in our audience was little to none. After doing a little research, I realised that the venue was a big contributing factor to this and we were unintentionally excluding people by holding it there.</p>
<p>The first thing I came across is that Muslims are forbidden from entering a pub by their religion, even if it's not to drink. Straight away this pushes away a chunk of our potential audience.</p>
<p>Then, I was discussing it with <a href="https://twitter.com/real_ate" target="_blank" rel="external noopener noreferrer">Chris Manson</a> and we realised that other people might feel uncomfortable or unsafe around alcohol and people drinking too. This includes women, alcoholics and minority groups.</p>
<p>Our event wasn't focused on drinking, it was about meeting people, learning and sharing knowledge. We didn't need the alcohol.</p>
<p>We found a great community venue and we made the pub a post-talk thing instead. Eventually we also realised that using some of the event budget for something that not everyone can be a part of is unfair and we made it an unofficial part of the event, using the budget to get vegan, vegetarian and gluten free options for our food.</p>
<p>Since making these changes, I've definitely noticed a much more mixed audience and I'm really pleased with this. I know we still have areas to work on, but this was a great move for us.</p>

    ]]></content>
  </entry>
	
  
  <entry>
    <title>2018 Retrospective</title>
    <link href="https://codefoodpixels.com/blog/2018/12/31/2018-retrospective/"/>
    <updated>2018-12-31T10:30:00Z</updated>
    <id>https://codefoodpixels.com/blog/2018/12/31/2018-retrospective/</id>
    <content type="html"><![CDATA[
      <p>2018 has been an eventful year, with more personal firsts and a number of successful personal projects!</p>
<p>If you want to see last year's post, you can find it <a href="https://codefoodpixels.com/blog/2017/12/24/2017-Retrospective/">here</a>.</p>
<h2 id="2018-aims">2018 Aims</h2>
<p>At the end of last year's post I set a few aims for this year. They were:</p>
<ul>
<li>More public speaking</li>
<li>More blogging</li>
<li>More learning</li>
<li>More open source projects</li>
</ul>
<p>I'll go into it a bit more below, but I feel like I've hit these aims.</p>
<h2 id="speaking">Speaking</h2>
<p>2018 was a good year for speaking for me.</p>
<p>I've spoken at a number of meetups, having given my home automation talk at Tech Nottingham and the Fusion Meetup in Birmingham in May and June; and then debuting my Web Bluetooth talk at LeedsJS in May too. To end my year of meetups, I spoke at the excellent FrontendNE, where I did a double feature of both talks.</p>
<p>In July I gave my home automation talk at the Fullstack London and ScotlandJS conferences, both of which I had a brilliant time at and met some fantastic people.</p>
<p>Before this year I'd never been to South America, but towards the end of the year I went twice to speak at two separate conferences. First, in October, I headed to Buenos Aires in Argentina to speak at Nodeconf Argentina and then in November I went to Medellín in Colombia to speak at JSConf Colombia. Both were amazing experiences in super interesting cities with more great people! I was also fortunate to be able to extend my time in both cities to explore and eat some amazing food!</p>
<h2 id="mental-health">Mental Health</h2>
<p>In August I wrote about taking steps to deal with my mental health better (you can read that post <a href="https://codefoodpixels.com/blog/2018/08/04/Opening-up-about-my-mental-health/">here</a>), and I've made some progress.</p>
<p>I went through the &quot;depression recovery course&quot; and while it didn't help, it was a step in the right direction.</p>
<p>A few weeks ago I started weekly counselling sessions. While I'm still unsure about it, I've spoken about some of the issues that I struggle with and talking about it has been a relief in itself.</p>
<h2 id="work">Work</h2>
<p>This year we've released some great improvements and features to the Sky Bet site, and I'm proud of the work that we've done.</p>
<p>I'm still learning stuff all the time and I'm still really enjoying the chances I get to mentor.</p>
<h2 id="leedsjs">LeedsJS</h2>
<p>This year we've had a load of amazing talks on both technical and non-technical topics at LeedsJS.</p>
<p>This year I decided to stop Q&amp;A, and I feel that this has been a success. You can read more about this decision <a href="https://codefoodpixels.com/blog/2018/05/06/Why-LeedsJS-Doesn-t-Have-Q-A-Anymore/">here</a>.</p>
<p>I also wrote about our giveaway process <a href="https://codefoodpixels.com/blog/2018/04/27/Community-Group-Giveaways/">here</a>. Since this post, I've also started selecting a second winner from any tweets made with the LeedsJS hashtag during the event.</p>
<p>Finally, I made the decision at the end of this year that I'm going to move LeedsJS away from Meetup.com during the next 6 months. They charge $90 for 6 months for what I feel to be a inflexible and sub-par platform. I'm going to be moving to using the LeedsJS website, using a ticketing system like Tito and a mailing list system like Mailchimp.</p>
<h2 id="personal-projects">Personal Projects</h2>
<p>At the end of last year and the beginning of this year, I built an ESP8266 based, JavaScript powered thermostat to control my heating. Until I moved house in July, this controlled my heating. The real test of this is when I went on a trip to Scotland in February and set it to the minimum temperature. It successfully controlled my heating and my pipes didn't freeze!</p>
<p>My next project was the LED display for my web bluetooth talk. I had real fun working on this and learning about bluetooth.</p>
<p>My final project of the year was automating my Christmas jumper. I connected it to the internet and allowed people to control it from around the world. I even did a live stream of it on Youtube. I hope to have a post written about it soon!</p>
<h2 id="2019-aims">2019 Aims</h2>
<p>Next year I'd like to do the following:</p>
<ul>
<li>More, varied talks - While I spoke at a number of conferences this year, it was the same talk I was giving last year. I do really enjoy this talk, but I'd love the opportunity to give a different talk!</li>
<li>More blogs - This was on last year's list too, but I feel like I need to post more frequently</li>
<li>Build a site to share my recipes - I want to share more of my cooking, and writing a site to share the recipes will really help with that!</li>
</ul>

    ]]></content>
  </entry>
	
  
  <entry>
    <title>Fired</title>
    <link href="https://codefoodpixels.com/blog/2018/09/07/fired/"/>
    <updated>2018-09-07T10:30:00Z</updated>
    <id>https://codefoodpixels.com/blog/2018/09/07/fired/</id>
    <content type="html"><![CDATA[
      <p>Friday the 7th September 2012 was a bad day for me. That was the day that I was fired from my first development job. It was the beginning of a 7 month unemployment period where I struggled with depression, impostor syndrome and I asked myself some tough questions.</p>
<h2 id="the-lead-up">The Lead-Up</h2>
<p>I'd been at the job for 15 months when I was fired and had spent a chunk of that time going through the disciplinary process for working too slow and not working to an expected quality.</p>
<p>Looking back, the situation was pretty ridiculous. I'd been hired as a junior developer (having never worked in the industry and having been completely self-taught), I was expected to work on projects completely on my own (including testing my own work) and I was turned down whenever I asked for help. I'd stayed late evenings and weekends to work on projects, trying to get them finished and to the level of quality that was expected, but it didn't help.</p>
<p>Throughout the disciplinary process, I was told that I needed to work faster and improve the quality of my work, but was never given the help I needed to do this. This eventually led to me being fired.</p>
<h2 id="the-aftermath">The Aftermath</h2>
<p>Immediately after being told I was fired, I collected my things. I couldn't face my now ex-coworkers and rushed my way through the office, out to my car and left.</p>
<p>I drove for about 15 minutes before deciding to stop off at a supermarket. The second I parked up and switched the engine off, I broke down and started crying. I'd fucked up, I was a failure.</p>
<p>When I finally got home, I told my housemate, friends and parents what had happened. I felt super low and afterwards, I just sat on the sofa, watched TV and ate junk food.</p>
<p>My friends helped me that evening, we hung out and got drunk. The support was hugely appreciated.</p>
<h2 id="the-months-following">The Months Following</h2>
<p>In the following months, depression hit me hard. I was burning through my savings to support myself and I was applying for any job I felt that I could do, but I was getting rejected from everything.</p>
<p>I had nothing to do during the day besides look for jobs. My life was empty. This meant that I started going to bed later and waking up later. Eventually, I got to the stage that I was going to bed at 6am and waking up at 1pm.</p>
<p>Impostor syndrome added itself to the mix. I'd been discovered as a fraud and had been fired. The question of whether I should keep trying to be a professional developer weighed heavy on my mind.</p>
<p>The interviews that I did get were tough. It's really difficult to convince someone to hire you when you're plagued by impostor syndrome. It's also really hard to be honest about being fired, so I tried to avoid it and talk around it. Looking back, this probably didn't work in my favour and was likely a mark against me.</p>
<h2 id="christmas">Christmas</h2>
<p>I was really fortunate that my parents offered to take my sister, her fiancé and I on holiday to Cyprus for Christmas that year. It was great to get a bit of a break and to focus on relaxing a little.</p>
<p>It was also an opportunity for me to reflect. I seriously considered my future and what I wanted to do. I came to the decision that I truly enjoyed development and wanted to keep trying to make a career out of it. I still felt like a fraud, but I didn't know what else I was any good at.</p>
<h2 id="new-year">New Year</h2>
<p>In the new year, I took some steps to get my life back on track. After spending months living only off my savings, I'd run out of money. I signed up for unemployment benefit and housing benefit.</p>
<p>I pushed myself to apply for development jobs and got a few interviews, but I was still struggling to be open and honest about being fired. When I did start being honest about it, I was surprised by the reaction. Nobody felt that firing me was the right way to go about things, which made me feel more comfortable about speaking about it.</p>
<p>After a few more months of job searching and interviewing, I finally managed to get a job offer. The hiring manager throughout the process made me feel super comfortable and that meant I could be completely honest.</p>
<h2 id="career-attempt-2">Career Attempt 2</h2>
<p>I started my new job on the 2nd of April 2013, just under 7 months from when I'd been fired. The impostor syndrome was still strong, but I had a second attempt at my career.</p>
<p>I'd managed to join a company with a very supportive development team. From the first day, I was learning and growing with the help of my colleagues. I stayed at that company for over 3 years and it really helped me develop my career.</p>
<p>Looking back, I'd have never thought I'd be where I am now. I'm working for a great company with a fantastic team, I'm speaking at conferences all over the world and I'm helping push the JavaScript community in Leeds forward by running LeedsJS. I still struggle with impostor syndrome every now and then, but I'm leaps and bounds beyond where I thought I'd be.</p>
<h2 id="reflections">Reflections</h2>
<p>Overall I think it was a pretty ridiculous situation. I nearly ended up taking a completely different career path, although I have no idea what that might have been. I struggled with some deep depression and impostor syndrome. I completely destroyed my sleeping patterns. It was a life-changing situation, but in the end, it all worked out for me. I'm incredibly lucky.</p>
<p>I hope you never have to go through this experience, but if you do then please know that you're not the first. Many of us have experienced this and made it through, you can too.</p>
<p>Although it really fucking sucks.</p>

    ]]></content>
  </entry>
	
  
  <entry>
    <title>Opening Up About My Mental Health</title>
    <link href="https://codefoodpixels.com/blog/2018/08/04/opening-up-about-my-mental-health/"/>
    <updated>2018-08-04T10:30:00Z</updated>
    <id>https://codefoodpixels.com/blog/2018/08/04/opening-up-about-my-mental-health/</id>
    <content type="html"><![CDATA[
      <p>I've tried to start writing this post a few times. Mental health is a difficult topic to speak about in itself, and struggling with your own mental health can make it even more difficult.</p>
<p>While this post is difficult to write, I want to be open about it. I hope that this can help other people feel like they can talk about their mental health too.</p>
<p>Since I was in my early teenage years, I've struggled with low moods and low self-esteem. My way of dealing with it has been to either to accept it and put up with feeling like that, or ignore it and carry on.</p>
<p>But a couple of months ago, I found out some things that made me realise that I'd not been coping with my mental health properly and that my way to deal with it wasn't great.</p>
<p>I decided that I needed to speak with a doctor and get some help with it. One of the effects of my struggle with mental health was that my low self-esteem meant that I didn't want to put my problems on other people. Looking back at it now this is ridiculous, but it meant that I hadn't been registered with a doctor since I moved out of my parents' house. My first step was to get this sorted.</p>
<p>I found that you can do a large portion of the registration process online, which hugely reduced the barrier for me. After doing the online part, I had to go in and organise an appointment with the nurse to finalise everything. The whole process took a few weeks in total.</p>
<p>After registering, I could book an appointment. Again, this was something I could do online which made me feel way more comfortable doing it.</p>
<p>A few weeks later, I had my appointment with the doctor. She was very friendly and made me feel comfortable discussing the way that I felt and the issues I was having. In the weeks leading up to the appointment, I'd started creating a list on my phone of things that I wanted to mention and this really helped me too.</p>
<p>The doctor said that there was nothing that she could do directly, but told me to use the Leeds IAPT self-referral system. This is an online process where you fill out some forms and questionnaires about the issues that you're facing. It took me about half an hour, but I filled it out on my phone during the bus ride to work after the appointment.</p>
<p>The questionnaires ask some questions such as how often you feel low and how often you felt little interest in doing things. This can be difficult to judge yourself on, especially if you've been living with a mental health issue for a while and it has become a somewhat normal part of your life. It also states to only look at the previous 2 weeks, which can greatly skew the outcome if you've had a good couple of weeks.</p>
<p>Due to the number of people that refer themselves, the IAPT says that it may be up to 5 weeks before they contact you. For me, I was contacted by email about 3 weeks later but was told that based on my results they may not be the most suitable service. As I'd had a couple of good weeks before I'd filled out the questionnaire, they asked me to fill it out again but as if I'd had a couple of bad weeks.</p>
<p>Shortly after I'd emailed back my updated questionnaire, I got a phone call from them. I was told that based on my results I had moderately severe depression and they gave me a few options.</p>
<p>First, they told me about the online resources they offer so that people can try and help themselves. Next, I was told about a couple of short group-based courses that try and give people an understanding of how to cope and deal with depression. Finally, I was told about one-to-one therapy.</p>
<p>As we discussed it, I didn't feel like the online resources would help me so we discounted that. I was then told that the one-to-one therapy has a 9-month waiting list and that by going on one of the courses, I join the waiting list anyway.</p>
<p>One of the key differences between the courses for me was how involved I have to be as a participant. I'm still very uncomfortable speaking about my mental health in person, especially to a group of strangers. This made me opt to go for the &quot;depression recovery course&quot;, which is a low-involvement course that's split into weekly 90 minute sessions over a period of 6 weeks. Because of my prior commitments to speaking at the Fullstack and ScotlandJS conferences, I couldn't start the next available course, so I was signed up to the following one. I start on the 28th of August.</p>
<p>I'm still unsure how much it'll actually help me, but I'm trying to be open minded and I'll hopefully learn as much as I can. If it ends up not helping, then at least I tried and I can see what the next step is. It's a start.</p>
<p>Finally, I'm really thankful to everyone who's been supportive and helpful so far. Being comfortable and able to talk about a difficult topic like mental health with someone helps a lot and I'm incredibly lucky that I have friends, co-workers and an excellent manager that I've been able to talk about it with.</p>
<p><em>Thanks to <a href="https://bethnorth.wordpress.com/" target="_blank" rel="external noopener noreferrer">Beth North</a> for reading through this before I posted it</em></p>

    ]]></content>
  </entry>
	
  
  <entry>
    <title>Why LeedsJS Doesn&#39;t Have Q&amp;A Anymore</title>
    <link href="https://codefoodpixels.com/blog/2018/05/06/why-leedsjs-doesnt-have-qanda-anymore/"/>
    <updated>2018-05-06T10:30:00Z</updated>
    <id>https://codefoodpixels.com/blog/2018/05/06/why-leedsjs-doesnt-have-qanda-anymore/</id>
    <content type="html"><![CDATA[
      <p>The day before April's LeedsJS meetup, I decided to try not having Q&amp;A after the talks in favour of encouraging people to come and chat to the speakers directly. There were a variety of reasons as to why I felt that this would be the best route, and it seems to have been a success!</p>
<h2 id="previous-experiences">Previous experiences</h2>
<p>Before April's event, we'd always had Q&amp;A after the talks. Over the 3 years that I've been involved in LeedsJS, I've experienced a few reoccurring issues with the Q&amp;A part of the talks.</p>
<h3 id="the-question-designed-to-show-off-how-much-the-asker-knows">The question designed to show off how much the asker knows</h3>
<p>This is one of my least favourite things I've had happen. Someone asks a question but puts it in a way that is intended to try and show that the asker knows more about the topic than the speaker.</p>
<p>This really sucks because the speaker has just given up a huge chunk of their time to share their knowledge and teach others. The asker has not and is basically stroking their own ego.</p>
<h3 id="i-have-this-specific-problem-with-my-code-fix-it-for-me">&quot;I have this specific problem with my code, fix it for me&quot;</h3>
<p>This is another one I really hate. The asker in this instance is basically asking the speaker to do work for them for free, on top of the work they had to do to prepare and deliver the talk.</p>
<p>What makes this worse for me is that it's likely that the asker came to the event with this question in mind, planning to ask the speaker to do this free work.</p>
<p>It's also disrespectful to the audience, they don't care about your specific code issues and they have no context for them anyway.</p>
<h3 id="more-of-a-statement-than-a-question-but">&quot;More of a statement than a question, but...&quot;</h3>
<p>I feel this is another example of the asker stroking their own ego. Rather than ask a question, the &quot;asker&quot; makes a statement, usually trying to correct something the speaker said or giving their opinion. To me this is hugely disrespectful, especially considering the time and effort that the speaker has put in.</p>
<h3 id="the-question-thats-largely-irrelevant-to-the-talk">The question that's largely irrelevant to the talk</h3>
<p>This usually happens when someone focuses on a small aspect of the talk that wasn't really relevant to the central point. It may be of interest to the asker but isn't related to what the speaker wanted people to take away from the talk or even something that the speaker has an opinion about.</p>
<h3 id="the-joke-question">The joke question</h3>
<p>While most people like a joke, unfortunately people aren't very original when it comes to thinking one up. Often when there's a joke question about a talk, the speaker has heard it multiple times already. To them, the humour has likely worn off and become tedious, especially if it's something related to them and not the talk.</p>
<h3 id="a-decent-chunk-of-the-audience-probably-doesnt-care">A decent chunk of the audience probably doesn't care</h3>
<p>While there can still be good questions in a Q&amp;A, it's still usually something that only a handful of your audience actually wants to know more about. The rest of your audience is becoming disengaged and just wants your event to progress. <a href="https://codefoodpixels.com/blog/2018/05/06/why-leedsjs-doesnt-have-qanda-anymore/#the-question-that's-largely-irrelevant-to-the-talk">The question that's largely irrelevant to the talk</a> and the <a href="https://codefoodpixels.com/blog/2018/05/06/why-leedsjs-doesnt-have-qanda-anymore/#i-have-this-specific-problem-with-my-code-fix-it-for-me">&quot;I have this specific problem with my code, fix it for me&quot;</a> also fall into this category.</p>
<h2 id="additional-factors">Additional factors</h2>
<p>On top of the things I've seen happen before at LeedsJS, there are some things that I also see as areas where Q&amp;A falls down or can be an issue. This stuff is either from my own experience as an organiser/speaker/attendee or what I've learned from others through discussions.</p>
<h3 id="the-speaker-is-put-on-the-spot">The speaker is put on the spot</h3>
<p>For some folks, being put on the spot to answer something that they've not been able to prepare for is anxiety-inducing and something that they want to avoid.</p>
<p>You may argue &quot;you're expected to know this stuff&quot; but I disagree, talks aren't only for experts. They're for sharing experiences and ideas, for inspiring folks to try something and for encouraging people to learn too. It's fine to not know the answer to a question, but being asked to admit this on stage in front of an audience is likely something that people want to avoid.</p>
<p>Similarly, you may argue &quot;you can't prepare for everything in life&quot;, but giving a talk is an already stressful situation that someone has spent a lot of time on preparation for. Adding a wildcard element into that is unnecessary extra stress.</p>
<h3 id="asking-questions-can-be-daunting">Asking questions can be daunting</h3>
<p>After publishing this post, <a href="https://twitter.com/NicolaClareFox/status/993107406879064064" target="_blank" rel="external noopener noreferrer">Nic on Twitter</a> mentioned that some folks don't like asking questions in front of a room full of people. This is an aspect that I'd forgotten to mention but one that I've experienced myself.</p>
<p>Public speaking is one of the most common fears that people have and in this situation asking a question is public speaking. This means that many people won't want to ask a question, no matter how much they want to know the answer. It can also mean that people can leave the talk feeling frustrated and confused, which is the opposite of what a speaker wants.</p>
<h3 id="conversations-are-more-meaningful">Conversations are more meaningful</h3>
<p>In my experience, the questions asked usually need some sort of back and forth to get an answer that both sides are happy with. This is really difficult to do when the speaker is on stage or at the front of the room and the asker is amongst a sea of faces.</p>
<p>Having a conversation allows the speaker and asker to be on the same page and get an answer that they're both happy with. On top of this, the speaker can ask other people questions when in a conversation situation, meaning that everyone can learn something.</p>
<h3 id="it-drags-down-the-end-of-the-talk">It drags down the end of the talk</h3>
<p>Often when a speaker ends their talk, they end it with something like a point that they want the audience to think about or with a big finish to wow the audience. The mood is then brought down by the Q&amp;A and usually ends at the lowest point of lingering silence to see if there are any more questions.</p>
<h2 id="what-were-doing-instead">What we're doing instead</h2>
<p>At LeedsJS we have 2 talks split by a 20 minute break. Instead of Q&amp;A, I encourage people to come and chat with the speaker after the talk. This means that only those who are interested in learning more can be part of the conversation instead of everyone being forced into it.</p>
<p>During this time I make sure I'm around the speaker for support and so that I can step in if someone is being rude or if there's an issue.</p>
<p>The conversations after the talks at the April event were great, the speaker and attendees all seemed to be enjoying the conversations. There seemed to be a lot of back and forth and there was some great discussion about parts of the talk or things related to it.</p>
<p>I've declared the trial at April's event a success. Q&amp;A is not coming back to LeedsJS.</p>
<h2 id="what-prompted-this">What prompted this?</h2>
<p>This has been something that's been in my head for a while, but a few things recently brought it to the front of my mind.</p>
<p>Firstly, I was picked to speak at <a href="http://scotlandjs.com/" target="_blank" rel="external noopener noreferrer">ScotlandJS 2018</a>, which has something they call the &quot;discussion track&quot;. After every 3 talks, there's a 20 minute break where the speakers from those talks will be in an advertised location for people to come and chat with them.</p>
<p>I also attended the JSHeroes conference the week before April's LeedsJS event. While the conference was great and the organisers tried to make the Q&amp;A section as engaging as possible, it still suffered many of the issues I've mentioned in this post. This is not the fault of the organisers, the work they did was fantastic!</p>
<p>Another thing was that Kitze shared his &quot;<a href="https://github.com/kitze/awesome-conference-practices" target="_blank" rel="external noopener noreferrer">awesome conference practices</a>&quot; document. There was some healthy discussion about Q&amp;A as part of that and I realised that other people shared my views on Q&amp;A.</p>

    ]]></content>
  </entry>
	
  
  <entry>
    <title>Community Group Giveaways</title>
    <link href="https://codefoodpixels.com/blog/2018/04/27/community-group-giveaways/"/>
    <updated>2018-04-27T10:30:00Z</updated>
    <id>https://codefoodpixels.com/blog/2018/04/27/community-group-giveaways/</id>
    <content type="html"><![CDATA[
      <p>At LeedsJS we've been lucky enough to have sponsors willing to offer up prizes for us to give away to our attendees. We've tried a couple of ways to give them away and I thought I'd write up a post to discuss them.</p>
<h2 id="where-to-get-prizes">Where to get prizes</h2>
<p>I guess this should be the first step, because you need to have stuff to be able to give away stuff. I know of a few ways to do this:</p>
<ol>
<li>
<p>Ask companies to give you stuff. When I first got involved in LeedsJS I reached out through Twitter to one of the developer advocates at Google and after a few emails I had some Google merchandise to give away.</p>
</li>
<li>
<p>Company sponsorship schemes. A few companies have proper schemes where they'll give merchandise or their products away to community groups. A couple of examples are <a href="https://www.jetbrains.com/community/support/#section=communities" target="_blank" rel="external noopener noreferrer">Jetbrains</a> and <a href="https://community.github.com/" target="_blank" rel="external noopener noreferrer">GitHub</a>.</p>
</li>
<li>
<p>Get sponsors to buy you stuff. If there are companies interested in sponsoring your events, an alternative to getting them to buy the food or drinks is to ask them to buy some prizes. This could be books, subscription codes or software licenses, but that's something to discuss with the sponsor.</p>
</li>
<li>
<p>Conferences will sometimes give you a ticket to give away in return for a bit of promotion, so it may be worth asking. They will also sometimes have a discount code that you can make available to your members.</p>
</li>
</ol>
<h2 id="ways-to-give-your-stuff-away">Ways to give your stuff away</h2>
<p>With LeedsJS we've tried a couple of ways to give our prizes away.</p>
<h3 id="challenges">Challenges</h3>
<p>Originally we started setting challenges around the main talk, so if we had a talk about Angular then the challenge would be to build something with Angular.</p>
<p>We'd get a couple of entries around some of the more interesting topics, but usually struggled to get anybody to enter. Ultimately we gave up with challenges because of this.</p>
<p>I do think this is a strong idea, but I think it would be better suited to framework or library specific groups because a lot of the topics will be based on the foundation of that framework or library whereas LeedsJS covers JavaScript in general.</p>
<h3 id="prize-draws">Prize draws</h3>
<p>The current way that we're doing our giveaways at LeedsJS is through a prize draw. As part of the introduction to the event, I have a slide with a link to a Google form for attendees to fill out. At the end of the event I then run a script attached to the form that will pick a random entrant as the winner.</p>
<p>If you feel like this is the right approach for your group, then I have a template form that you can use to get started. The form has the script attached to it already, so all you have to do is make a copy, customise it to suit your giveaway and then share it. When you come to select your winner, click on the puzzle piece in the top right, select &quot;Choose random responder&quot;. You'll need to auth with your Google account so that it can read the data. You can get the form from <a href="https://drive.google.com/open?id=1LGGcDjoS8_yWZbQU9blBz9AdTJX9z4Gq" target="_blank" rel="external noopener noreferrer">this Google folder</a>.</p>
<p>Currently this form only asks for the entrant's name, but after a discussion with my friend and former co-organiser <a href="https://twitter.com/real_ate" target="_blank" rel="external noopener noreferrer">Chris Manson</a> I may update the one for LeedsJS to ask for some feedback about the event/group too.</p>

    ]]></content>
  </entry>
	
  
  <entry>
    <title>TIL: document.querySelectorAll returns a NodeList</title>
    <link href="https://codefoodpixels.com/blog/2018/02/06/til-documentqueryselectorall-returns-a-nodelist/"/>
    <updated>2018-02-06T10:30:00Z</updated>
    <id>https://codefoodpixels.com/blog/2018/02/06/til-documentqueryselectorall-returns-a-nodelist/</id>
    <content type="html"><![CDATA[
      <p>Modern browsers have the native selector engine <code>document.querySelectorAll</code> which is really useful for easily finding elements. I had assumed that it returned an Array but I've just found out that I was wrong and it returns a NodeList.</p>
<p>A NodeList is still a collection, but it doesn't have the Array prototype methods such as <code>.filter</code> or <code>.map</code>. You can convert the NodeList to an array using <code>Array.from()</code> or you can use items from the Array prototype using <code>.call</code> like this:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">var</span> elements <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">".content__article"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />elements <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>elements<span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">el</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br />  <span class="token comment">// Your filter code goes here</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>

    ]]></content>
  </entry>
	
  
  <entry>
    <title>Blackboxing Scripts In Chrome DevTools</title>
    <link href="https://codefoodpixels.com/blog/2018/02/05/blackboxing-scripts-in-chrome-devtools/"/>
    <updated>2018-02-05T10:30:00Z</updated>
    <id>https://codefoodpixels.com/blog/2018/02/05/blackboxing-scripts-in-chrome-devtools/</id>
    <content type="html"><![CDATA[
      <p>Chrome DevTools has a feature called blackboxing which allows you to ignore certain scripts when debugging. When a script is blackboxed, it's hidden from the call stack pane and you don't step into it when stepping through the code. This is really useful when debugging code that uses frameworks or libraries such as Ember, React or jQuery.</p>
<p>There are a few ways to blackbox a script:</p>
<h2 id="blackboxing-in-settings">Blackboxing in settings</h2>
<p><picture><source type="image/avif" srcset="https://codefoodpixels.com/static/images/igJwZr9Hms-250.avif 250w, https://codefoodpixels.com/static/images/igJwZr9Hms-600.avif 600w, https://codefoodpixels.com/static/images/igJwZr9Hms-800.avif 800w, https://codefoodpixels.com/static/images/igJwZr9Hms-1000.avif 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/webp" srcset="https://codefoodpixels.com/static/images/igJwZr9Hms-250.webp 250w, https://codefoodpixels.com/static/images/igJwZr9Hms-600.webp 600w, https://codefoodpixels.com/static/images/igJwZr9Hms-800.webp 800w, https://codefoodpixels.com/static/images/igJwZr9Hms-1000.webp 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/jpeg" srcset="https://codefoodpixels.com/static/images/igJwZr9Hms-250.jpeg 250w, https://codefoodpixels.com/static/images/igJwZr9Hms-600.jpeg 600w, https://codefoodpixels.com/static/images/igJwZr9Hms-800.jpeg 800w, https://codefoodpixels.com/static/images/igJwZr9Hms-1000.jpeg 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/png" srcset="https://codefoodpixels.com/static/images/igJwZr9Hms-250.png 250w, https://codefoodpixels.com/static/images/igJwZr9Hms-600.png 600w, https://codefoodpixels.com/static/images/igJwZr9Hms-800.png 800w, https://codefoodpixels.com/static/images/igJwZr9Hms-1000.png 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><img alt="The blackboxing tab in the settings section of Chrome DevTools" loading="lazy" decoding="async" src="https://codefoodpixels.com/static/images/igJwZr9Hms-250.jpeg" width="1000" height="554" /></picture></p>
<ol>
<li>
<p>In the settings section, go to the &quot;Blackboxing&quot; tab</p>
</li>
<li>
<p>Click &quot;Add pattern...&quot;</p>
</li>
<li>
<p>In the text box, enter either a script name or a regex pattern that will match scripts that you want to blackbox.</p>
</li>
<li>
<p>Click add</p>
</li>
</ol>
<h2 id="blackboxing-in-the-editor-pane-of-the-sources-section">Blackboxing in the editor pane of the sources section</h2>
<p><picture><source type="image/avif" srcset="https://codefoodpixels.com/static/images/05r_8toVf9-250.avif 250w, https://codefoodpixels.com/static/images/05r_8toVf9-600.avif 600w, https://codefoodpixels.com/static/images/05r_8toVf9-800.avif 800w, https://codefoodpixels.com/static/images/05r_8toVf9-1000.avif 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/webp" srcset="https://codefoodpixels.com/static/images/05r_8toVf9-250.webp 250w, https://codefoodpixels.com/static/images/05r_8toVf9-600.webp 600w, https://codefoodpixels.com/static/images/05r_8toVf9-800.webp 800w, https://codefoodpixels.com/static/images/05r_8toVf9-1000.webp 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/jpeg" srcset="https://codefoodpixels.com/static/images/05r_8toVf9-250.jpeg 250w, https://codefoodpixels.com/static/images/05r_8toVf9-600.jpeg 600w, https://codefoodpixels.com/static/images/05r_8toVf9-800.jpeg 800w, https://codefoodpixels.com/static/images/05r_8toVf9-1000.jpeg 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/png" srcset="https://codefoodpixels.com/static/images/05r_8toVf9-250.png 250w, https://codefoodpixels.com/static/images/05r_8toVf9-600.png 600w, https://codefoodpixels.com/static/images/05r_8toVf9-800.png 800w, https://codefoodpixels.com/static/images/05r_8toVf9-1000.png 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><img alt="The blackboxing item in the editor pane context menu" loading="lazy" decoding="async" src="https://codefoodpixels.com/static/images/05r_8toVf9-250.jpeg" width="1000" height="994" /></picture></p>
<ol>
<li>
<p>In the sources section, open the file you want to blackbox</p>
</li>
<li>
<p>Right click in the editor pane</p>
</li>
<li>
<p>Select &quot;Blackbox script&quot;</p>
</li>
</ol>
<h2 id="blackboxing-in-the-call-stack-pane-of-the-sources-section">Blackboxing in the call stack pane of the sources section</h2>
<p><picture><source type="image/avif" srcset="https://codefoodpixels.com/static/images/CQH0eifVZc-250.avif 250w, https://codefoodpixels.com/static/images/CQH0eifVZc-600.avif 600w, https://codefoodpixels.com/static/images/CQH0eifVZc-800.avif 800w, https://codefoodpixels.com/static/images/CQH0eifVZc-1000.avif 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/webp" srcset="https://codefoodpixels.com/static/images/CQH0eifVZc-250.webp 250w, https://codefoodpixels.com/static/images/CQH0eifVZc-600.webp 600w, https://codefoodpixels.com/static/images/CQH0eifVZc-800.webp 800w, https://codefoodpixels.com/static/images/CQH0eifVZc-1000.webp 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/jpeg" srcset="https://codefoodpixels.com/static/images/CQH0eifVZc-250.jpeg 250w, https://codefoodpixels.com/static/images/CQH0eifVZc-600.jpeg 600w, https://codefoodpixels.com/static/images/CQH0eifVZc-800.jpeg 800w, https://codefoodpixels.com/static/images/CQH0eifVZc-1000.jpeg 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/png" srcset="https://codefoodpixels.com/static/images/CQH0eifVZc-250.png 250w, https://codefoodpixels.com/static/images/CQH0eifVZc-600.png 600w, https://codefoodpixels.com/static/images/CQH0eifVZc-800.png 800w, https://codefoodpixels.com/static/images/CQH0eifVZc-1000.png 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><img alt="The blackboxing item in the call stack pane context menu" loading="lazy" decoding="async" src="https://codefoodpixels.com/static/images/CQH0eifVZc-250.jpeg" width="1000" height="750" /></picture></p>
<ol>
<li>
<p>When paused on a breakpoint, go to the sources section</p>
</li>
<li>
<p>In the call stack pane, right click on a function from the file you want to blackbox</p>
</li>
<li>
<p>Select &quot;Blackbox script&quot;</p>
</li>
</ol>

    ]]></content>
  </entry>
	
  
  <entry>
    <title>Learning About Burnout By Crashing My Car</title>
    <link href="https://codefoodpixels.com/blog/2018/01/15/learning-about-burnout-by-crashing-my-car/"/>
    <updated>2018-01-15T10:30:00Z</updated>
    <id>https://codefoodpixels.com/blog/2018/01/15/learning-about-burnout-by-crashing-my-car/</id>
    <content type="html"><![CDATA[
      <p>Today marks 4 years since I crashed and wrote off my car while driving to work one morning due to being burnt out. At the time I didn't really realise I was burnt out or even what burnout was.</p>
<p>The company I was working for at the time had a huge industry show at the end of January every year. This was a chance to show off new features and demo versions of future features and led to working a lot of overtime on evenings and weekends in the run up to it.</p>
<p>My commute was at least an hour and a half of driving each way and I was tired and mentally drained meaning I wasn't paying I couldn't fully pay attention, no matter how hard I tried. As I was about half way to work one morning, I came over a ridge to find a van doing a turn in the road with a queue of 4 cars stopped in front of me. I tried to slow down but couldn't react quick enough and swerved towards the kerb to avoid hitting the Porsche at the end of the queue. My car mounted the high kerb and slid along it on it's underside past 3 of the cars and scraped along the side of the last one before landing back on the road.</p>
<p>I pulled the car over to the side of the road and checked that the people in the other cars were alright. I then checked out my car and it was in awful shape. The front axel had snapped when I hit the high kerb, the driver's side door was dented and scratched from scraping along the last car, and it was leaking fluids due to the underside being damaged when sliding along the kerb.</p>
<p>A passing police officer pulled over to help when he saw what had happened. He helped us make sure that we'd done everything we needed to, called a recovery truck to collect my car and gave me a lift to the bus station so I could get to work. I'm super thankful that he stopped as I wasn't in a great mental state to deal with this and his guidance was extremely helpful.</p>
<p>Having totalled my car, my commute increased to 2 and a half hours each way and I had to cope with this as well as the high work load and having to deal with the insurance company. I was already thinking about moving to live somewhere closer but the longer commute made it more urgent, so I also had to deal with finding somewhere to live and packing.</p>
<p>I found somewhere, moved there in early February and took a few days off to relax, recover and reflect. I really felt the difference and realised that I was burnt out and that burnout has real effects. I'd been spending my own time, exhausting myself and putting my own health at risk for a company that in the long run didn't really care about me.</p>
<p>I stopped doing overtime and started fighting to stop the causes of it. My personal time since then has been spent doing stuff for me, whether that's playing games, working on my open source projects, cooking or doing pixel art.</p>
<p>Burnout has real effects. Take care of yourself, your health is important.</p>

    ]]></content>
  </entry>
	
  
  <entry>
    <title>HTTPS With A Custom Domain On GitHub Pages</title>
    <link href="https://codefoodpixels.com/blog/2018/01/02/https-with-a-custom-domain-on-github-pages/"/>
    <updated>2018-01-02T10:30:00Z</updated>
    <id>https://codefoodpixels.com/blog/2018/01/02/https-with-a-custom-domain-on-github-pages/</id>
    <content type="html"><![CDATA[
      <p>While GitHub pages supports HTTPS for sites using the github.io domain, it doesn't support it for custom domains. I've had a few people ask how I've achieved HTTPS with a custom domain on GitHub pages, so I felt I should write a post on it.</p>
<h2 id="setting-up-github-pages">Setting Up GitHub Pages</h2>
<p>The first thing we have to do is configure the repo on GitHub.</p>
<ol>
<li>
<p>Open the &quot;settings&quot; page on your repo and scroll down to the &quot;GitHub Pages&quot; section</p>
</li>
<li>
<p>Select the source that your GitHub pages site will be built from. There are a few options here:</p>
<ul>
<li>
<p>gh-pages branch - This means you can use a separate branch on your project just for your documentation.</p>
</li>
<li>
<p>master branch - This means that the content of your master branch is used. I use this for this website.</p>
</li>
<li>
<p>master branch /docs folder - This uses the contents of a &quot;docs&quot; folder in the root of your master branch for the site content.</p>
</li>
<li>
<p>None - This disables GitHub pages for the repo.</p>
</li>
</ul>
</li>
<li>
<p>Set your custom domain in the &quot;Custom domain&quot; section</p>
</li>
</ol>
<p><strong>Note:</strong> The &quot;Enforce HTTPS&quot; box gets disabled when you add a custom domain. This is fine as we'll be enforcing HTTPS in the next section.</p>
<h2 id="adding-https-with-cloudflare">Adding HTTPS with Cloudflare</h2>
<p>To add HTTPS to our GitHub pages custom domain we'll be using the free tier of <a href="https://www.cloudflare.com/" target="_blank" rel="external noopener noreferrer">Cloudfare</a>.</p>
<h3 id="adding-the-dns-records">Adding The DNS Records</h3>
<p>During the process of adding your site, you'll be shown a list of DNS records for your domain. Alternatively you can go to the &quot;DNS&quot; section of the dashboard.</p>
<p>You need to add <code>CNAME</code> record for <code>www</code> that redirects to your GitHub pages URL. For me that means I have it set to <code>lukeb-uk.github.io</code>. Make sure that the traffic is set to go through Cloudflare with the orange cloud.</p>
<p>You could also add your root domain instead of <code>www</code> and this would mean it redirects to GitHub pages when you hit the root domain. This is how I have it set up.</p>
<h3 id="enabling-https">Enabling HTTPS</h3>
<p>On the dashboard, go to the &quot;Crypto&quot; section. In the SSL section you should select &quot;Full&quot; and <strong>not</strong> &quot;Full (strict)&quot;.</p>
<p>Further down the page there's a setting for &quot;Always use HTTPS&quot;. Set this to &quot;On&quot;. This means that any HTTP connections will get forced to HTTPS.</p>
<p>You now have a site hosted on GitHub Pages that uses a custom domain and HTTPS!</p>
<h2 id="cloudflare-caching">Cloudflare Caching</h2>
<p>Cloudflare also has a CDN (content delivery network) allowing optimised delivery of content to your visitors. By default HTML content isn't cached, but you can add a page rule to do this.</p>
<p>In the &quot;Page Rules&quot; section of the dashboard, create a page rule. In the URL box, put your domain followed by an asterisk. For me that would be <code>https://lukeb.co.uk/*</code>. Then in the settings section, select &quot;Cache Level&quot; from the drop down and then set the cache level to &quot;Cache Everything&quot;. Save and deploy this rule.</p>
<p>You can also enable Cloudflare's Always Online™ feature that will serve your site's static pages from their cache if GitHub has an outage. You can enable this in the &quot;Caching&quot; section of the dashboard.</p>

    ]]></content>
  </entry>
	
  
  <entry>
    <title>2017 Retrospective</title>
    <link href="https://codefoodpixels.com/blog/2017/12/24/2017-retrospective/"/>
    <updated>2017-12-24T10:30:00Z</updated>
    <id>https://codefoodpixels.com/blog/2017/12/24/2017-retrospective/</id>
    <content type="html"><![CDATA[
      <p>2017 has been a good year for me with some successful projects and some personal firsts. I felt I should do a little retrospective on this stuff, partially so I can come back and look at it in the future, partially as a celebration. It's not in any particular order.</p>
<h2 id="speaking">Speaking</h2>
<p>This year I was lucky enough to be picked to speak at JSConf Budapest (<a href="https://www.youtube.com/watch?v=dAqKa3waNx8" target="_blank" rel="external noopener noreferrer">video</a>), my first time speaking at a large conference. The event was awesome and I'm so happy that I got to be part of such an excellent lineup and meet so many great people. Also Budapest is a beautiful city, I need to go back.</p>
<p>I was also asked to speak at the Leeds Testing Atelier, a community-run testing conference, where I spoke about snapshot testing (<a href="https://www.youtube.com/watch?v=uzfydKkSAuc" target="_blank" rel="external noopener noreferrer">video</a>). Again, this was a great experience!</p>
<p>Rounding out my speaking this year, I was asked at short notice to speak GDG Devfest Coimbra. I jumped at the opportunity to speak again and was glad that I did! The event was a mix of web, Android and a few other things, all really interesting! Again I got to meet some fantastic people and have some great conversations. I didn't get to spend a lot of time in Portugal, but I definitely want to go back.</p>
<p>I'm really fortunate that my manager has been super supportive as I've been doing this, allowing me to take the time off work without having to use holiday as well as giving some great feedback. My friends and co-workers have been really supportive too.</p>
<h2 id="leedsjs">LeedsJS</h2>
<p>LeedsJS is the JavaScript community group that I run in Leeds and this has been our best year so far!</p>
<p>Towards the end of 2016 we tried having 2 talks per event and I felt it worked so well that this year we carried on with it!</p>
<p>We've had great talks on JavaScript topics such as React, Swagger and PWAs as well as some more human topics such as mental health and continuous delivery.</p>
<p>We also became an alcohol-free event as well as trying to get some more inclusive food choices (such as vegan and gluten free options). I've noticed that our attendees are a bit more diverse as a result, which is what I was aiming for!</p>
<h2 id="work">Work</h2>
<p>I've been at Sky Betting &amp; Gaming for 18 months now and it's flown by. I'm extremely fortunate to be working with a great team.</p>
<p>I've learned a lot over the past year and being allowed the time to learn stuff while working has been fantastic. As a result, I finally did some work with React this year. Having the opportunity to mentor has been amazing too!</p>
<p>A huge milestone was releasing the responsive site to 100% of customers. My team has been working on this the entire time that I've worked there, taking an experimental approach to make sure we got it right. I'm super proud of the work we've done.</p>
<h2 id="personal-projects">Personal Projects</h2>
<p>Earlier in this year I released <a href="https://github.com/codefoodpixels/jscad-includify" target="_blank" rel="external noopener noreferrer">jscad-includify</a> which is a Node module that builds the includes for a <a href="https://openjscad.org/" target="_blank" rel="external noopener noreferrer">JSCAD</a> project into one file. This was a great opportunity to learn more about ASTs as well as having a go at building a utility that works as both a cli tool and a Node module.</p>
<p>As a result of the work I did on jscad-includify, I got interested in snapshot testing. As I couldn't find a tool that would let me do snapshot testing with TAP (the Node test framework that I like), I ended up writing <a href="https://github.com/codefoodpixels/tapshot" target="_blank" rel="external noopener noreferrer">tapshot</a>. This was an interesting challenge as I learned a lot about snapshot testing and had to ensure it was well tested itself.</p>
<p>I also rebuilt this website with the aim of keeping it more up to date and to blog more. You can read more about the website <a href="https://codefoodpixels.com/blog/2017/11/12/New-Website/">here</a>.</p>
<p>Throughout the year I've also been working on my automation bot <a href="https://github.com/Woodhouse" target="_blank" rel="external noopener noreferrer">Woodhouse</a>. I've not added a lot of major functionality and have mainly been working on tidying up the version 2 code, but I have been working on automating my heating over the past couple of months and will hopefully have a post up on that soon.</p>
<h2 id="mental-health">Mental Health</h2>
<p>This year I've been far more aware of and open about my mental health.</p>
<p>I've taken 2 separate weeks to clean and declutter my house. During this time I cleared a load of junk out of my house, cleaned, donated a load of stuff to charity and set up a work area so that I can build stuff. This was with the aim of making my house a place I feel more at home in and somewhere I can feel comfortable.</p>
<p>A co-worker and I also pitched for Mind to be Sky Betting &amp; Gaming's charity of the year for 2018. The winner will be announced early next year so fingers crossed that we win!</p>
<h2 id="2018-aims">2018 Aims</h2>
<p>I have no real concrete aims for 2018, but I have a few rough ideas:</p>
<ul>
<li>More public speaking - I've really enjoyed giving talks and want to do it more. I've got a plan for my next talk and will be starting work soon!</li>
<li>More blogging - I want to try and share as much information as I can, as well as increasing my written communication skills.</li>
<li>More learning</li>
<li>More open source projects</li>
</ul>

    ]]></content>
  </entry>
	
  
  <entry>
    <title>Conditional Breakpoints In Chrome DevTools</title>
    <link href="https://codefoodpixels.com/blog/2017/12/05/conditional-breakpoints-in-chrome-devtools/"/>
    <updated>2017-12-05T10:30:00Z</updated>
    <id>https://codefoodpixels.com/blog/2017/12/05/conditional-breakpoints-in-chrome-devtools/</id>
    <content type="html"><![CDATA[
      <p>A really useful feature that I often find myself using in Chrome DevTools is conditional breakpoints. Conditional breakponts allow you to only pause execution when a certain expression evaluates to true, meaning that you can pause it when you want it to.</p>
<h2 id="how-to-use-conditional-breakpoints-in-chrome-devtools">How to use conditional breakpoints in Chrome DevTools</h2>
<ol>
<li>
<p>In the &quot;Sources&quot; panel in Chrome DevTools, open the JavaScript file that you want to debug.</p>
</li>
<li>
<p>Right click on the line number of the line you want to add the breakpoint to and select &quot;Add conditional breakpoint&quot;.</p>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://codefoodpixels.com/static/images/Mzx3OCLJMA-250.avif 250w, https://codefoodpixels.com/static/images/Mzx3OCLJMA-600.avif 600w, https://codefoodpixels.com/static/images/Mzx3OCLJMA-800.avif 800w, https://codefoodpixels.com/static/images/Mzx3OCLJMA-1000.avif 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/webp" srcset="https://codefoodpixels.com/static/images/Mzx3OCLJMA-250.webp 250w, https://codefoodpixels.com/static/images/Mzx3OCLJMA-600.webp 600w, https://codefoodpixels.com/static/images/Mzx3OCLJMA-800.webp 800w, https://codefoodpixels.com/static/images/Mzx3OCLJMA-1000.webp 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/jpeg" srcset="https://codefoodpixels.com/static/images/Mzx3OCLJMA-250.jpeg 250w, https://codefoodpixels.com/static/images/Mzx3OCLJMA-600.jpeg 600w, https://codefoodpixels.com/static/images/Mzx3OCLJMA-800.jpeg 800w, https://codefoodpixels.com/static/images/Mzx3OCLJMA-1000.jpeg 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/png" srcset="https://codefoodpixels.com/static/images/Mzx3OCLJMA-250.png 250w, https://codefoodpixels.com/static/images/Mzx3OCLJMA-600.png 600w, https://codefoodpixels.com/static/images/Mzx3OCLJMA-800.png 800w, https://codefoodpixels.com/static/images/Mzx3OCLJMA-1000.png 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><img alt="The breakpoint context menu. Add conditional breakpoint is the 3rd option" loading="lazy" decoding="async" src="https://codefoodpixels.com/static/images/Mzx3OCLJMA-250.jpeg" width="1000" height="289" /></picture></p>
<ol start="3">
<li>Add your conditional statement into the text box. Your condition must be truthy when you want it to break. In my example I want it to break when there is more than one asteroid in the list.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://codefoodpixels.com/static/images/QspPRDGaF2-250.avif 250w, https://codefoodpixels.com/static/images/QspPRDGaF2-600.avif 600w, https://codefoodpixels.com/static/images/QspPRDGaF2-800.avif 800w, https://codefoodpixels.com/static/images/QspPRDGaF2-1000.avif 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/webp" srcset="https://codefoodpixels.com/static/images/QspPRDGaF2-250.webp 250w, https://codefoodpixels.com/static/images/QspPRDGaF2-600.webp 600w, https://codefoodpixels.com/static/images/QspPRDGaF2-800.webp 800w, https://codefoodpixels.com/static/images/QspPRDGaF2-1000.webp 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/jpeg" srcset="https://codefoodpixels.com/static/images/QspPRDGaF2-250.jpeg 250w, https://codefoodpixels.com/static/images/QspPRDGaF2-600.jpeg 600w, https://codefoodpixels.com/static/images/QspPRDGaF2-800.jpeg 800w, https://codefoodpixels.com/static/images/QspPRDGaF2-1000.jpeg 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/png" srcset="https://codefoodpixels.com/static/images/QspPRDGaF2-250.png 250w, https://codefoodpixels.com/static/images/QspPRDGaF2-600.png 600w, https://codefoodpixels.com/static/images/QspPRDGaF2-800.png 800w, https://codefoodpixels.com/static/images/QspPRDGaF2-1000.png 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><img alt="The breakpoint condition box" loading="lazy" decoding="async" src="https://codefoodpixels.com/static/images/QspPRDGaF2-250.jpeg" width="1000" height="249" /></picture></p>
<ol start="4">
<li>When you run the code and the condition is met, the breakpoint will be fired and code execution will be paused!</li>
</ol>
<p><picture><source type="image/avif" srcset="https://codefoodpixels.com/static/images/KoQO6VqOnW-250.avif 250w, https://codefoodpixels.com/static/images/KoQO6VqOnW-600.avif 600w, https://codefoodpixels.com/static/images/KoQO6VqOnW-800.avif 800w, https://codefoodpixels.com/static/images/KoQO6VqOnW-1000.avif 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/webp" srcset="https://codefoodpixels.com/static/images/KoQO6VqOnW-250.webp 250w, https://codefoodpixels.com/static/images/KoQO6VqOnW-600.webp 600w, https://codefoodpixels.com/static/images/KoQO6VqOnW-800.webp 800w, https://codefoodpixels.com/static/images/KoQO6VqOnW-1000.webp 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/jpeg" srcset="https://codefoodpixels.com/static/images/KoQO6VqOnW-250.jpeg 250w, https://codefoodpixels.com/static/images/KoQO6VqOnW-600.jpeg 600w, https://codefoodpixels.com/static/images/KoQO6VqOnW-800.jpeg 800w, https://codefoodpixels.com/static/images/KoQO6VqOnW-1000.jpeg 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/png" srcset="https://codefoodpixels.com/static/images/KoQO6VqOnW-250.png 250w, https://codefoodpixels.com/static/images/KoQO6VqOnW-600.png 600w, https://codefoodpixels.com/static/images/KoQO6VqOnW-800.png 800w, https://codefoodpixels.com/static/images/KoQO6VqOnW-1000.png 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><img alt="The breakpoint has fired as there are 2 items in the list" loading="lazy" decoding="async" src="https://codefoodpixels.com/static/images/KoQO6VqOnW-250.jpeg" width="1000" height="331" /></picture></p>
<p>Because the condition is executed each time that breakpoint is checked, you could even have it run other code. A great example would be when you're trying to log out some input from a certain point in code, but can't edit the source (e.g. when debugging built/live code).</p>
<p><picture><source type="image/avif" srcset="https://codefoodpixels.com/static/images/2c_9djJmaI-250.avif 250w, https://codefoodpixels.com/static/images/2c_9djJmaI-600.avif 600w, https://codefoodpixels.com/static/images/2c_9djJmaI-800.avif 800w, https://codefoodpixels.com/static/images/2c_9djJmaI-1000.avif 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/webp" srcset="https://codefoodpixels.com/static/images/2c_9djJmaI-250.webp 250w, https://codefoodpixels.com/static/images/2c_9djJmaI-600.webp 600w, https://codefoodpixels.com/static/images/2c_9djJmaI-800.webp 800w, https://codefoodpixels.com/static/images/2c_9djJmaI-1000.webp 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/jpeg" srcset="https://codefoodpixels.com/static/images/2c_9djJmaI-250.jpeg 250w, https://codefoodpixels.com/static/images/2c_9djJmaI-600.jpeg 600w, https://codefoodpixels.com/static/images/2c_9djJmaI-800.jpeg 800w, https://codefoodpixels.com/static/images/2c_9djJmaI-1000.jpeg 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><source type="image/png" srcset="https://codefoodpixels.com/static/images/2c_9djJmaI-250.png 250w, https://codefoodpixels.com/static/images/2c_9djJmaI-600.png 600w, https://codefoodpixels.com/static/images/2c_9djJmaI-800.png 800w, https://codefoodpixels.com/static/images/2c_9djJmaI-1000.png 1000w" sizes="(min-width: 41.5rem) 37.5rem, 15.625rem" /><img alt="Using console.log within the expression to log out the length of the asteroid list" loading="lazy" decoding="async" src="https://codefoodpixels.com/static/images/2c_9djJmaI-250.jpeg" width="1000" height="188" /></picture></p>

    ]]></content>
  </entry>
	
  
  <entry>
    <title>New Website!</title>
    <link href="https://codefoodpixels.com/blog/2017/11/12/new-website/"/>
    <updated>2017-11-12T10:30:00Z</updated>
    <id>https://codefoodpixels.com/blog/2017/11/12/new-website/</id>
    <content type="html"><![CDATA[
      <p>My previous site hadn't been touched since late 2014 and was running using a custom PHP system I'd written that took markdown and produced the page. This was happening every time someone hit the page so was pretty inefficient.</p>
<p>As my focus is no longer on PHP, I wanted to move towards something JavaScript based. I didn't want to use a client side framework to do this, as the content is pretty static and I feel it would be needless to require JavaScript to present static content.</p>
<h2 id="static-site-generator">Static Site Generator</h2>
<p>I had a look around and chose <a href="https://hexo.io/" target="_blank" rel="external noopener noreferrer">Hexo</a> as a static site generator. Because this just outputs HTML files, I am using GitHub Pages to host it. The site consists of 2 repos:</p>
<ul>
<li>The first is the <a href="https://github.com/lukeb-uk/website-hexo" target="_blank" rel="external noopener noreferrer">website-hexo</a> repo. This contains the Hexo project that the site is generated from. The raw markdown files and EJS templates live in here.</li>
<li>The second is the <a href="https://github.com/lukeb-uk/website" target="_blank" rel="external noopener noreferrer">website</a> repo. This is where the generated site is pushed to and is then served using GitHub Pages.</li>
</ul>
<h2 id="automated-builds">Automated Builds</h2>
<p>I have automated the build process using Travis CI. Any time I push a commit up to the website-hexo repo, the site is generated and pushed to a new branch on the website repo. A pull request is made and a preview is pushed up to ZEIT's <a href="https://zeit.co/now" target="_blank" rel="external noopener noreferrer">Now</a>. The code for all this can be found in the <a href="https://github.com/lukeb-uk/website-hexo/blob/master/.travis.yml" target="_blank" rel="external noopener noreferrer">Travis config file</a> in the website-hexo repo.</p>
<h2 id="design-and-implementation">Design And Implementation</h2>
<p>For the actual website, I wanted to use pixel art in the design. I really enjoy creating pixel art and love the style visually. I looked at some retro games for UI inspiration and put together a design that I'm pretty happy with.</p>
<p>A lot of the actual layout is achieved with flexbox. I love how powerful flexbox is and what you can achieve with it. Although it does sort of remind me of the table layouts that were popular when I started developing.</p>
<p>I took the opportunity to add a little animation using CSS, as it's not something I've done much and wanted to try more. I feel it's worked well!</p>
<p>The main functionality of the site uses no JavaScript, however there is an easter egg that does. The only hint I'll give is that you should follow the instructions in the header.</p>

    ]]></content>
  </entry>
	
</feed>
