<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>The coding morsels of Luke Alex Davis</title><description>My RSS feed of coding snippets.</description><link>https://lukealexdavis.co.uk/</link><item><title>Morsel #29: ROM website name generator</title><link>https://lukealexdavis.co.uk/posts/undefined/</link><guid isPermaLink="true">https://lukealexdavis.co.uk/posts/undefined/</guid><description>Click R to pay respects</description><pubDate>Wed, 07 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;style&amp;gt;.rom-name {font-weight: 700; text-align: center; font-size: 2rem;}&amp;lt;/style&amp;gt;&lt;/p&gt;
&lt;p&gt;If you&apos;ve ever played video games using emulators, you may be familiar with ROM sites. Two of my favourite site names are Emuparadise and CDRomance. They give a very 2000s tropical/Aero Frutiger vibe and I thought “why not try to randomise some of those names myself?”&lt;/p&gt;
&lt;p&gt;And this is what I came up with. It&apos;s barebones and may or may not break but it&apos;s safe (as far as I can tell) and you can try it out. For now, it&apos;s just bold text in randomised web safe fonts but I&apos;m hoping to add different colours in at some point.&lt;/p&gt;
&lt;p&gt;I&apos;ve used a handful of words so if you have anymore suggestions, let me know!&lt;/p&gt;
&lt;p&gt;&amp;lt;button id=&quot;generate&quot; aria-label=&quot;Generate button&quot;&amp;gt;Generate&amp;lt;/button&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;rom-name&quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;script&amp;gt;&lt;/p&gt;
&lt;p&gt;let format = [&quot;CD&quot;, &quot;ROM&quot;, &quot;Emu&quot;, &quot;Software&quot;, &quot;BIOS&quot;, &quot;ISO&quot;, &quot;Disc&quot;, &quot;Disk&quot;, &quot;Floppy&quot;, &quot;HDD&quot;, &quot;SSD&quot;, &quot;SD&quot;];
let vibe = [&quot;Paradise&quot;, &quot;Utopia&quot;, &quot;Oasis&quot;, &quot;Bliss&quot;, &quot;Exotica&quot;, &quot;Island&quot;, &quot;Garden&quot;, &quot;Sanctuary&quot;, &quot;Romance&quot;, &quot;Passion&quot;, &quot;Devotion&quot;, &quot;Heaven&quot;, &quot;Divine&quot;, &quot;Angel&quot;, &quot;Sacred&quot;, &quot;Spirit&quot;];
let fonts = [&quot;Arial&quot;, &quot;Verdana&quot;, &quot;Tahoma&quot;, &quot;Trebuchet MS&quot;, &quot;Times New Roman&quot;, &quot;Georgia&quot;, &quot;Courier New&quot;, &quot;Brush Script MT&quot;];&lt;/p&gt;
&lt;p&gt;let romClass = document.querySelector(&apos;.rom-name&apos;);&lt;/p&gt;
&lt;p&gt;const element = document.querySelector(&quot;#myButton&quot;);&lt;/p&gt;
&lt;p&gt;function generateName() {
const formatRand = Math.floor(Math.random() * format.length);
const vibeRand = Math.floor(Math.random() * vibe.length);
const fontsRand = Math.floor(Math.random() * fonts.length);
let romName = &lt;code&gt;${format[formatRand]}${vibe[vibeRand]}&lt;/code&gt;;
romClass.style.fontFamily = fonts[fontsRand];
romClass.textContent = romName;
}&lt;/p&gt;
&lt;p&gt;let button = document.querySelector(&apos;#generate&apos;);
button.addEventListener(&quot;click&quot;, function() {
generateName();
})&lt;/p&gt;
&lt;p&gt;&amp;lt;/script&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Morsel #28: How I use lazy loading on Astro image sites</title><link>https://lukealexdavis.co.uk/posts/undefined/</link><guid isPermaLink="true">https://lukealexdavis.co.uk/posts/undefined/</guid><description>For when you want to keep things static and not use client-side JS.</description><pubDate>Tue, 30 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import IcebergNotes from &apos;../../components/IcebergNotes.astro&apos;;&lt;/p&gt;
&lt;p&gt;I have an image site called &lt;a href=&quot;https://coleccao.club/&quot;&gt;Coleçcao&lt;/a&gt; where I post pictures that I think are cool or inspirational (like a cross between Tumblr, Are.na, and Pinterest). As you might have noticed on this site, I try to avoid images as I rarely need them to illustrate my points.&amp;lt;sup&amp;gt;[&amp;lt;a href=&quot;#in-1&quot;&amp;gt;1&amp;lt;/a&amp;gt;]&amp;lt;/sup&amp;gt;&lt;/p&gt;
&lt;p&gt;But with a website that is 99% images, I need a solution that keeps page load as short as possible and that&apos;s where lazy loading comes in!&lt;/p&gt;
&lt;h2&gt;Lazy loading&lt;/h2&gt;
&lt;p&gt;If you&apos;re unaware, lazy loading is a way to delay fully loading an element until it becomes visible in the viewport (the visible part of your window). If it&apos;s not seen, it&apos;s not completely loaded.&amp;lt;sup&amp;gt;[&amp;lt;a href=&quot;#in-2&quot;&amp;gt;2&amp;lt;/a&amp;gt;]&amp;lt;/sup&amp;gt;.&lt;/p&gt;
&lt;p&gt;This is mainly used on images but you can lazy load iframes too (good for YouTube embeds).&lt;/p&gt;
&lt;p&gt;There are a few ways you can implement lazy loading but the main ones are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Using the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API&quot;&gt;Intersection Observer API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Using scroll, resize, or orientationchange event handlers&lt;/li&gt;
&lt;li&gt;Using a &amp;lt;code&amp;gt;loading&amp;lt;/code&amp;gt; attribute&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The first two need JavaScript. The last one is just a HTML attribute like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;img src=&quot;example.jpg&quot; alt=&quot;&quot; width=&quot;500&quot; height=&quot;500&quot; loading=&quot;lazy&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The idea is to load everything a user immediately sees and defer everything else until they bring it into view. It&apos;s a good thing and more people who use images and iframes should add it to their code (&lt;a href=&quot;https://calendar.perfplanet.com/2022/lazy-loading-lcp-images-why-does-this-anti-pattern-happen/&quot;&gt;properly&lt;/a&gt;)&lt;/p&gt;
&lt;h2&gt;Adding this without JavaScript... &lt;em&gt;kinda&lt;/em&gt;&lt;/h2&gt;
&lt;p&gt;Here&apos;s a brief overview of how Coleçcao works in terms of image display.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure style=&quot;text-align:center;&quot;&amp;gt;
&amp;lt;img src=&quot;/images/coleccao-screenshot.jpg&quot; width=&quot;600&quot; height=&quot;292&quot; alt=&quot;a screenshot of the Coleçcao website&quot; loading=&quot;lazy&quot; /&amp;gt;
&amp;lt;figcaption&amp;gt;This is lazy loaded too!&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;You get a main paginated feed on the homepage which has about 10 images arranged in rows—2 to 3 per row depending on their dimensions. But because of that dependency, I can&apos;t be sure how many images will be in the viewport at any time which means I won&apos;t know which images need to be preloaded and have a higher fetchpriority and which need to be lazy loaded.&lt;/p&gt;
&lt;p&gt;In the end, I figured 4 images would be fine to preload and the rest could be lazy loaded and I coded the homepage as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Layout title=&quot;Colecçao&quot;&amp;gt;
  &amp;lt;section&amp;gt;
    &amp;lt;div class=&quot;atom-list&quot;&amp;gt;
      {page.data.map((atom: any) =&amp;gt; (
      &amp;lt;figure class=&quot;atom-listing&quot;&amp;gt;
        &amp;lt;a href={`/atom/${atom.id}/`}&amp;gt;
          {page.data.indexOf(atom) &amp;lt; 4 ?
          &amp;lt;img src={atom.data.image} alt={atom.data.description} width={atom.data.dimensions[0]} height={atom.data.dimensions[1]} fetchpriority=&quot;high&quot; /&amp;gt; :
        		&amp;lt;img src={atom.data.image} alt={atom.data.description} width={atom.data.dimensions[0]} height={atom.data.dimensions[1]} loading=&quot;lazy&quot; /&amp;gt;
          }
        &amp;lt;/a&amp;gt;
        &amp;lt;figcaption&amp;gt;{atom.data.title}&amp;lt;/figcaption&amp;gt;
      &amp;lt;/figure&amp;gt;
      ))}
&amp;lt;!--Rest of code goes here --&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I use &lt;a href=&quot;https://docs.astro.build/en/guides/routing/#pagination&quot;&gt;Astro&apos;s pagination functionality&lt;/a&gt; to route all my images and then I map them using the page prop. But the key element is the JavaScript in the &lt;code&gt;a&lt;/code&gt; tag. It uses a ternary operator to say if the index of the mapped item is less than 4, then an &lt;code&gt;img&lt;/code&gt; tag should be displayed with &lt;code&gt;fetchpriority=&quot;high&quot;&lt;/code&gt;. Any item index of 4 or more should use the &lt;code&gt;loading=&quot;lazy&quot;&lt;/code&gt; attribute.&lt;/p&gt;
&lt;p&gt;So, yes, I still use JavaScript to make this happen but it&apos;s all in the pre-built code. When it comes time to build, all you get are static HTML tags with the correct attributes (🤞).&lt;/p&gt;
&lt;p&gt;This isn&apos;t the best implementation but it&apos;s a heuristic that works for me and at worst, maybe one image isn&apos;t lazy loaded when it should be. I best not tell the web performance church elders!&lt;/p&gt;
&lt;h2&gt;Why not use client-side JS?&lt;/h2&gt;
&lt;p&gt;Truth be told, the Intersection Observer API is perfectly cromulent for this and many people use it to great effect (I actually used it on a WordPress site when they messed up lazy loading a while back). But I don&apos;t want to rely on API code that could potentially break or that I have to maintain on the frontend. A simple ternary operator and some variables is quick and not that dirty. If anything breaks, it should be easier to fix and totally in my control.&lt;/p&gt;
&lt;h2&gt;Where to find the code&lt;/h2&gt;
&lt;p&gt;If you&apos;re interested to see the full implementation, &lt;a href=&quot;https://github.com/starchildluke/coleccao&quot;&gt;the code is on GitHub&lt;/a&gt;. As always, if you spot anything that I could improve, feel free to raise an issue and I will look at it but understand that I&apos;m not a web developer so be kind with your critiques.&lt;/p&gt;
&lt;p&gt;&amp;lt;IcebergNotes&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;sup id=&quot;in-1&quot;&amp;gt;1&amp;lt;/sup&amp;gt;I am considering some SVGs on some pages to give them a bit of decoration but they&apos;ll be super lightweight to keep things well-optimised.&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;sup id=&quot;in-2&quot;&amp;gt;2&amp;lt;/sup&amp;gt;There is a threshold for this and it actually goes a little outside of the window so some of it is loaded but not the full thing. &amp;lt;a href=&quot;https://web.dev/articles/browser-level-image-lazy-loading#distance-from-viewport&quot;&amp;gt;You can find out more in Google&apos;s documentation&amp;lt;/a&amp;gt;.&amp;lt;/p&amp;gt;
&amp;lt;/IcebergNotes&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Morsel #27: local apps for good</title><link>https://lukealexdavis.co.uk/posts/undefined/</link><guid isPermaLink="true">https://lukealexdavis.co.uk/posts/undefined/</guid><description>Sometimes the best inspiration comes from the people you love</description><pubDate>Thu, 25 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Since using &lt;a href=&quot;https://lukealexdavis.co.uk/morsels/morsel-16/&quot;&gt;marimo&lt;/a&gt;, I&apos;ve had an abundance of ideas for apps and it has even inspired me to reorganise my scripts into project folders (long overdue). But earlier today, I realised a lot of these apps were excuses to test features and get accustomed to marimo. This is fine and it helps me learn but the data and use cases aren&apos;t as meaningful.&lt;/p&gt;
&lt;p&gt;I looked through my old &lt;a href=&quot;https://jupyter.org/&quot;&gt;Jupyter notebooks&lt;/a&gt; and &lt;a href=&quot;https://streamlit.io/gallery&quot;&gt;Streamlit apps&lt;/a&gt; this week and spotted one that I made for my mum who suffers from &lt;a href=&quot;https://www.mayoclinic.org/diseases-conditions/ulcerative-colitis/symptoms-causes/syc-20353326&quot;&gt;colitis&lt;/a&gt;. She was struggling with finding meals or enjoying food based on her newly acquired dietary requirements. So I decided to build an app with some food recipe data where you click a button and a random dish pops up. Nothing too strenuous or complex, just a way to help someone I love.&lt;/p&gt;
&lt;p&gt;And then it hit me: sometimes the best inspiration comes from the people you love. If I know someone close to me has a problem and an app could help solve it, it&apos;s a lot easier to build something, have them test it, and iterate until it&apos;s right and functional.&lt;/p&gt;
&lt;p&gt;And there may well be an app or service already out there that can do this sort of thing—my mum could Google the best meals for someone with colitis—but it&apos;s fun to build apps and to certain specifications, just like creating your own recipes with a loved one or a patch quilt.&lt;/p&gt;
&lt;p&gt;marimo has been great so far and I think with this new realisation, it&apos;ll be perfect for more local apps for good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Related&lt;/strong&gt;: &lt;a href=&quot;/morsels/morsel-17/&quot;&gt;Small and specific wins the AI race&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Morsel #26: vibe recursion</title><link>https://lukealexdavis.co.uk/posts/undefined/</link><guid isPermaLink="true">https://lukealexdavis.co.uk/posts/undefined/</guid><description>Garbage in, garbage out and back in again</description><pubDate>Sun, 14 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;What if you wrote a prompt (Prompt n) that asked the LLM to write a better prompt (Prompt n+1) than this prompt (Prompt n) and to ask for a better prompt to ask for later (which would be Prompt n+3). And then you repeat the process over and over to see the results.&lt;/p&gt;
&lt;p&gt;Would it become asymptotic and reach a point where it couldn&apos;t produce a &quot;better&quot; prompt or would it go wild and lose quality and meaning?&lt;/p&gt;
</content:encoded></item><item><title>Morsel #25: templates are meant to be flexible</title><link>https://lukealexdavis.co.uk/posts/undefined/</link><guid isPermaLink="true">https://lukealexdavis.co.uk/posts/undefined/</guid><description>The more you explore, the more you&apos;ll discover in presenting your findings</description><pubDate>Fri, 12 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When I started in tech SEO, my audits were in the classic Google Docs checklist format. I remember getting feedback on one of my first ones and I felt awful because I had no idea how I was supposed to do it any differently; reports were written as documents and that was that.&lt;/p&gt;
&lt;p&gt;But over time, I learnt that data representation can take on so many different forms and perhaps a Google Doc isn&apos;t the right way all the time. I now present my audit findings in a spreadsheet with a prioritisation framework adapted from &lt;a href=&quot;https://cxl.com/blog/better-way-prioritize-ab-tests/&quot;&gt;CXL&apos;s PXL framework&lt;/a&gt;. I list the following things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Audit issues&lt;/li&gt;
&lt;li&gt;Issues categorised by type,&lt;/li&gt;
&lt;li&gt;How/why it may affect performance (note: it might not so establishing that it&apos;s a &lt;a href=&quot;https://www.thoughtworks.com/en-gb/insights/topic/sensible-defaults&quot;&gt;sensible default&lt;/a&gt; would go here!)&lt;/li&gt;
&lt;li&gt;How many URLs it affects (or could be a sitewide issue)&lt;/li&gt;
&lt;li&gt;A solution (written as a direct action)&lt;/li&gt;
&lt;li&gt;A list of 10 criteria that should influence the importance of the fix, with scores ranging from 0–2&lt;/li&gt;
&lt;li&gt;A final priority score (adding up the criteria scores)&lt;/li&gt;
&lt;li&gt;A low/medium/high ratiing (for stakeholders how think scores are arbitrary)&lt;/li&gt;
&lt;li&gt;If developers are needed, how long may the work take (a very rough estimate with wide time ranges)&lt;/li&gt;
&lt;li&gt;Who is responsible for the work&lt;/li&gt;
&lt;li&gt;Next steps or notes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I made this to remove a lot of the guesswork out of auditing as our priorities may not be the same as other peoples&apos; priorities. This idea came after years of doing things in different ways and I may end up changing this or adapting it further (the current format has only recently been changed to include more things).&lt;/p&gt;
&lt;p&gt;For web performance audits, I ditched documents for slide decks. I present a basic agenda, what all the metrics mean and what data I&apos;ve looked at, and outline each Core Web Vitals metric and the issues with recommendations. This is a way to meet stakeholders wherever they are in terms of web performance knowledge and ensure developers know what they need to do to investigate and fix issues. It also helps if you can show how these issues affect revenue and conversions!&lt;/p&gt;
&lt;p&gt;My point is with anything we present, templates need to be flexible to get everyone on board and to understand what the problem is and how to fix it. A document may do this but maybe a slide deck can, or an organised spreadsheet, or even some cool interactive data visualisations. That fluidity is what will get people engaged and interested in making the Web a better place.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Semi-related:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/posts/some-caveats-to-seo-and-web-perf/&quot;&gt;Some caveats to SEO and web performance&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Morsel #24: Python Morsels</title><link>https://lukealexdavis.co.uk/posts/undefined/</link><guid isPermaLink="true">https://lukealexdavis.co.uk/posts/undefined/</guid><pubDate>Sat, 23 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Today, I found out about a learning programme called &lt;a href=&quot;https://www.pythonmorsels.com/&quot;&gt;Python Morsels&lt;/a&gt;. Created by &lt;a href=&quot;https://treyhunner.com/&quot;&gt;Trey Hunner&lt;/a&gt;, Python Morsels allows people to improve their Python skills for 30 minutes each week with a series of exercises to work through and screencasts to watch.&lt;/p&gt;
&lt;p&gt;When I started my own &quot;morsels&quot; as a way to share random Web/coding things I&apos;d made or learnt, I had no idea about this project but it was nice to find it.&lt;/p&gt;
</content:encoded></item><item><title>Morsel #23: Letter swapper</title><link>https://lukealexdavis.co.uk/posts/undefined/</link><guid isPermaLink="true">https://lukealexdavis.co.uk/posts/undefined/</guid><pubDate>Tue, 19 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import IcebergNotes from &apos;../../components/IcebergNotes.astro&apos;;
import Swapped from &apos;../../components/Swapped.astro&apos;;&lt;/p&gt;
&lt;p&gt;&amp;lt;section style=&quot;margin-bottom:2rem;font-size:1.5rem;font-weight: 700;&quot;&amp;gt;
&amp;lt;form id=&quot;swap-first-letter-form&quot;&amp;gt;
&amp;lt;label for=&quot;text-to-swap&quot;&amp;gt;Enter your strings, one per line: &amp;lt;/label&amp;gt;
&amp;lt;input type=&quot;text&quot; id=&quot;text-to-swap&quot; name=&quot;text-to-swap&quot; required=&quot;&quot;&amp;gt;
&amp;lt;/input&amp;gt;
&amp;lt;button type=&quot;submit&quot;&amp;gt;Swap those letters!&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;section id=&quot;swapped-result&quot; style=&quot;margin:1rem 0;font-size:1.5rem;font-weight: 700;&quot;&amp;gt;&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;What is this?&lt;/h2&gt;
&lt;p&gt;This is a HTML form that takes an input (a string) and returns a list of that string with the first letter replaced with every letter of the alphabet and a selection of English &lt;a href=&quot;https://en.wikipedia.org/wiki/Digraph_(orthography)&quot;&gt;digraphs&lt;/a&gt; and &lt;a href=&quot;https://en.wikipedia.org/wiki/Trigraph_(orthography)&quot;&gt;trigraphs&lt;/a&gt;. For example, if I put my name in (Luke Davis), it would return an HTML list saying &quot;Auke Davis&quot;, &quot;Cuke Davis&quot;, &quot;Duke Davis&quot; etc. But I&apos;d also get names like &quot;Fluke Davis&quot; and &quot;Spuke Davis&quot;.&lt;/p&gt;
&lt;h2&gt;Why did you make this?&lt;/h2&gt;
&lt;p&gt;I saw a friend&apos;s Bluesky name was a variation of &quot;bell hooks&quot; which led me to think of something for Tony Hawk. I ended up with &quot;Phony Hawk&quot; and realised it&apos;d make a funny nickname for a Pokémon called Gholdengo which uses a golden surfboard to travel around. Then I wondered, what if you could generate a list of names with the first part replaced for meme purposes.&lt;/p&gt;
&lt;p&gt;Ideological rabbit holes are fun!&lt;/p&gt;
&lt;h2&gt;How the code works&lt;/h2&gt;
&lt;p&gt;I used the base of the &lt;a href=&quot;/morsels/morsel-13/&quot;&gt;Thumbnail Grabber&lt;/a&gt; so when you submit a string, the script checks if anything is there and replaces the first letter the string with each letter of the alphabet. It also matches the casing, whether that&apos;s upper or lower case. Finally, it outputs it in a neat HTML list.&lt;/p&gt;
&lt;p&gt;In terms of JavaScript, there are &lt;code&gt;querySelector&lt;/code&gt;&apos;s to grab the value of the input and add it to the output element and two functions for swapping the first letters (including a nested &lt;code&gt;flatMap&lt;/code&gt; with a regular &lt;code&gt;map&lt;/code&gt; inside).&lt;/p&gt;
&lt;p&gt;I did use AI for the nested &lt;code&gt;flatMap&lt;/code&gt; as I couldn&apos;t figure out how to replace the first letter which suggested I need to learn that pattern better but everything else was a mix of reworking what I made before and using StackOverflow lol&amp;lt;sup&amp;gt;[&amp;lt;a href=&quot;#in-1&quot;&amp;gt;1&amp;lt;/a&amp;gt;]&amp;lt;/sup&amp;gt;&lt;/p&gt;
&lt;h2&gt;How this could be improved&lt;/h2&gt;
&lt;p&gt;I&apos;m sure something will come up but feel free to &lt;a href=&quot;https://github.com/starchildluke/astro/issues&quot;&gt;raise an issue on GitHub&lt;/a&gt; if you have any suggestions!&lt;/p&gt;
&lt;p&gt;&amp;lt;IcebergNotes&amp;gt;
&amp;lt;sup id=&quot;in-1&quot;&amp;gt;1&amp;lt;/sup&amp;gt;This is &amp;lt;strong&amp;gt;NOT&amp;lt;/strong&amp;gt; &amp;lt;a href=&quot;/posts/some-genuine-thoughts-vibe-coding/&quot;&amp;gt;vibe coding&amp;lt;/a&amp;gt;, mainly because AI use was for one function because I couldn&apos;t figure it out but also because I was very conscious about what I was coding.)
&amp;lt;/IcebergNotes&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;Swapped /&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Morsel #22: you can animate SVGs</title><link>https://lukealexdavis.co.uk/posts/undefined/</link><guid isPermaLink="true">https://lukealexdavis.co.uk/posts/undefined/</guid><description>How did I not know you could animate SVGs? It&apos;s right there in the spec!</description><pubDate>Mon, 07 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;TIL: you can animate SVGs and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/animate&quot;&gt;it&apos;s been in the SVG specification all along&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There are three elements:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;animate&amp;gt; &amp;lt;!--Provides a way to animate an attribute of an element over time.--&amp;gt;
&amp;lt;animateMotion&amp;gt; &amp;lt;!--Provides a way to animate an element along a motion path.--&amp;gt;
&amp;lt;animateTransform&amp;gt; &amp;lt;!--Provides a way to animate an element as a transformation (e.g. skewing, rotation, scaling).--&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The good thing about this is that, depending on your use case, it makes for a great substitute for GIFs which can balloon in size depending on the frame rate and the color depth. With a SVG, you could get a small file size and the best resolution possible. Even better if it&apos;s small enough to use inline!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://koaning.io/posts/svg-gifs/&quot;&gt;Thanks to Vincent Warmerdam for the tip&lt;/a&gt; (who gives an example using a couple of tools to make it).&lt;/p&gt;
</content:encoded></item><item><title>Morsel #21: meta values</title><link>https://lukealexdavis.co.uk/posts/undefined/</link><guid isPermaLink="true">https://lukealexdavis.co.uk/posts/undefined/</guid><description>How&apos;s your head? Full of values!</description><pubDate>Tue, 17 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I saw a post talking about a jokey meta tag and remembered that you can add any kind of meta tag to your site if you want. It won&apos;t be recognised by any search engine but it won&apos;t break your site either.&lt;/p&gt;
&lt;p&gt;So I&apos;ve added my own to this site:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;meta name=&quot;values&quot; content=&quot;Free Palestine, Black Lives Matter, and Trans Lives Matter&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The idea is to show what I stand for in the code itself (besides the Palestine flag at the bottom of my page). And it makes sense to have it in the head.&lt;/p&gt;
&lt;p&gt;I wondered if other people joined in whether I could create a directory of sites for it. Feel free to email or message me if you&apos;re interested. All you&apos;d need to do is add the code above but replace the content value with your own values. I&apos;d do it via GitHub and you&apos;d have to submit a PR for me to approve (so nothing shitty gets in).&lt;/p&gt;
</content:encoded></item><item><title>Morsel #20: meta http-equiv status</title><link>https://lukealexdavis.co.uk/posts/undefined/</link><guid isPermaLink="true">https://lukealexdavis.co.uk/posts/undefined/</guid><description>A cool idea but perhaps impractical?</description><pubDate>Wed, 11 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import IcebergNotes from &apos;../../components/IcebergNotes.astro&apos;;&lt;/p&gt;
&lt;p&gt;An interesting question came up at work today where I was asked about a line of code and whether it&apos;d work. The code in question was this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;meta http-equiv=&quot;Status&quot; content=&quot;401&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The idea was to use this as a replacement for server-side code to serve a 401 status code. I initially said &quot;yeah, sure&quot; until I took a second look and realised I&apos;d not seen the &lt;code&gt;Status&lt;/code&gt; value before.&lt;/p&gt;
&lt;h2&gt;meta http-equiv&lt;/h2&gt;
&lt;p&gt;A quick thing on http-equiv. It&apos;s an attribute that simulates an HTTP response header (equiv is short for &apos;equivalent&apos;) and is mostly used for things called meta refreshes where the page refreshes or redirects on the client&apos;s side instead of the usual server-side refresh/redirect (like a 3xx status code). In that case, it&apos;d look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;meta http-equiv=&quot;refresh&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&apos;s generally recommended not to do this for redirects but if you don&apos;t have server access, it&apos;s a close-enough substitute for the user, even if the UX isn&apos;t ideal.&lt;/p&gt;
&lt;p&gt;In the case of &lt;code&gt;http-equiv=&quot;Status&quot;&lt;/code&gt; it looked familiar but not &lt;em&gt;quite&lt;/em&gt;, hence why I had to take a second look. A quick Google returned one exact result but it wasn&apos;t from MDN or even W3Schools. &lt;a href=&quot;https://indieweb.org/meta_http-equiv_status&quot;&gt;It came from Indie Web&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;meta http-equiv status is an HTML extension specification that adds the &quot;Status&quot; value to the existing meta element&apos;s http-equiv attribute as a method for representing the HTTP Status code to be returned by a web server serving that HTML document, and for consuming applications like webmention receivers to parse and interpret as such for use-cases such as representing a 410 Gone deleted resource on static hosting that often disallow setting HTTP headers directly.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This sounded fair in theory but when I checked the only example in the doc, it returned a 200 status code instead of a 401. What&apos;s more, the requirements were quite specific, both for the server and the &quot;consuming application&quot;. Because of all these factors, I figured it wasn&apos;t a feasible solution.&lt;/p&gt;
&lt;p&gt;Another thing to note: server activity happens before page load so offering a status &lt;em&gt;during&lt;/em&gt; page load won&apos;t work. It&apos;s too late by then and this will miss the mark. As you saw above, meta refreshes only &lt;em&gt;simulate&lt;/em&gt; server responses and would cause issues for users coming from a search engine expecting one page only for it to refresh on load. If the 401 was to work, the page would load as normal—serving a 200—and then suddenly give a 401 error page (with or without the code).&lt;/p&gt;
&lt;h2&gt;A word on 410 vs. 404&lt;/h2&gt;
&lt;p&gt;A 404 status code tells a server the resource in question could not be found. There are a number of reasons why that might be the case but the main ones are that the page was removed or the URL was incorrect in someway. A 410 status code tells a server the resource in question is permanently gone (literally). Now you might be thinking why we use 404s if we delete pages when 410 is probably more appropriate. If you deleted, it&apos;s gone, right? Well, 404 is a softer default. You could delete something and decide to reinstate it and it&apos;s easier to bring it back to the same URL and have a search engine pick it up and reinstate it to the index. But a 410 is meant to be a permanent goodbye.&lt;/p&gt;
&lt;p&gt;If you really to nuke a page, 410s are objectively better and quicker to get picked up by Google and dropped from the index (&lt;a href=&quot;https://www.searchenginejournal.com/googles-john-mueller-clarifies-404-410-confusion-for-seo/513576/&quot;&gt;but by a negligible amount, according to John Mueller&lt;/a&gt;). However, in most cases, 404 does the job. What&apos;s more, 404 is better supported with CMS&apos;s and other frameworks as they usually offer templates and functionality to serve them should a page gets deleted. 410s tend not to have the same level of support so you may have to create this routes and pages manually (if you even have access to do so). And you might not even see the difference once you have.&lt;/p&gt;
&lt;p&gt;So yeah, if you&apos;re getting traffic from a regularly visited external link and that page was deleted and it&apos;s causing server issues, go for a 410. Otherwise, 404 works just fine.&lt;/p&gt;
&lt;p&gt;&amp;lt;IcebergNotes&amp;gt;
&amp;lt;p&amp;gt;I&apos;m likely to be missing some nuances, exceptions, and quirks of 410s, as well as the http-equiv I&apos;ve quoted. If you have a &quot;well, actually&quot; to throw my way, feel free to email me but be kind :)&amp;lt;/p&amp;gt;
&amp;lt;/IcebergNotes&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Morsel #19: a marimo notebook as a blog post editor?</title><link>https://lukealexdavis.co.uk/posts/undefined/</link><guid isPermaLink="true">https://lukealexdavis.co.uk/posts/undefined/</guid><pubDate>Fri, 18 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;style&amp;gt;
.shdw {
--shadow-color: 0deg 0% 63%;
box-shadow: 0.3px 0.5px 0.7px hsl(var(--shadow-color) / 0.36),
0.8px 1.6px 2px -0.8px hsl(var(--shadow-color) / 0.36),
2.1px 4.1px 5.2px -1.7px hsl(var(--shadow-color) / 0.36),
5px 10px 12.6px -2.5px hsl(var(--shadow-color) / 0.36);
}
&amp;lt;/style&amp;gt;&lt;/p&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/starchildluke/blogpost_editor&quot;&gt;GitHub link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://starchildluke.github.io/blogpost_editor/notebooks/blogpost_editor.html&quot;&gt;Try the notebook!&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;For when an IDE or Markdown editor doesn&apos;t quite cut it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is a &lt;a href=&quot;https://marimo.io/&quot;&gt;marimo notebook&lt;/a&gt; that functions as a blog post editor.&lt;/p&gt;
&lt;p&gt;It works best in app mode with a grid layout to mimic the UI of a minimalist post editor but you can follow the usual flow in edit mode too. I use &lt;a href=&quot;https://astro.build/&quot;&gt;Astro&lt;/a&gt; for my website so I built this to make blog editing more visual. It comes with a section to add my metadata and the body text, view a preview (which renders the Markdown), and when I click the &quot;Post&quot; button, it generates an MD file with all the correct Markdown (so far)&lt;/p&gt;
&lt;p&gt;&amp;lt;figure style=&quot;margin-bottom: 1rem;&quot;&amp;gt;
&amp;lt;img src=&quot;/images/marimo-blogpost-editor.webp&quot; alt=&quot;the UI for my blog post editor app&quot; loading=&quot;lazy&quot; class=&quot;shdw&quot;&amp;gt;
&amp;lt;figcaption&amp;gt;Ain&apos;t it pretty?&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;h2&gt;Why did I make this&lt;/h2&gt;
&lt;p&gt;After reading about &lt;a href=&quot;https://koaning.io/posts/a-flask-app-to-help-me-write-blogposts/&quot;&gt;Vincent Warmerdam&apos;s cool blog post editor made with Flask&lt;/a&gt;, I tried it myself. But I soon realised that my version didn&apos;t work as well and I know nothing about Flask so rather than debug someone else&apos;s concept, I&apos;d make my own. I use &lt;a href=&quot;https://www.sublimetext.com/&quot;&gt;Sublime Text&lt;/a&gt; normally but I&apos;ve also tried &lt;a href=&quot;https://obsidian.md/&quot;&gt;Obsidian&lt;/a&gt; and other Markdown editors in the past. This was a nice idea because it gives more immediate feedback after I&apos;ve written something rather than saving and switching to my browser (which doesn&apos;t always update immediately).&lt;/p&gt;
&lt;h2&gt;Things I could do&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;At the moment the blog editor just saves the Markdown file in the directory that your notebook sits in so I will add a file browser to select the folder my blog post should go in.&lt;/li&gt;
&lt;li&gt;This only works for regular posts but I have different post types such as shorter blogs called &amp;lt;span lang=&quot;pt&quot;&amp;gt;recortes&amp;lt;/span&amp;gt;, posts that are just lists, morsels which are code-based blogs and others. I could add a dropdown of different frontmatter options as they all require unique metadata and then I won&apos;t have to manually edit anything.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Morsel #18: drumples</title><link>https://lukealexdavis.co.uk/posts/undefined/</link><guid isPermaLink="true">https://lukealexdavis.co.uk/posts/undefined/</guid><pubDate>Thu, 03 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I made a marimo notebook that pulls 3 random drum samples and lets you play them along with a random song from a Spotify playlist(s).&lt;/p&gt;
&lt;p&gt;&amp;lt;figure style=&quot;aspect-ratio: 2 / 1;&quot;&amp;gt;
&amp;lt;img src=&quot;/images/drumples.webp&quot; alt=&quot;the UI for my drumples app&quot; loading=&quot;eager&quot;&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt;
&amp;lt;strong&amp;gt;Disclaimer&amp;lt;/strong&amp;gt;
&amp;lt;/summary&amp;gt;
&amp;lt;p&amp;gt;I used a local LLM (large language model) to generate the code for the Spotify playlist song randomiser, in the name of vibe coding. This was intended to be a personal project and everything worked with minimal changes. You&apos;re welcome to change things up if they&apos;re suboptimal.&amp;lt;/p&amp;gt;
&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/starchildluke/drumples&quot;&gt;GitHub link&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Requirements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://marimo.io/&quot;&gt;marimo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.spotify.com/documentation/web-api&quot;&gt;Spotify API credentials&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I sadly can&apos;t demo it as I don&apos;t want to make Spotify API access public so you&apos;ll have to sign up yourself. But once you have those credentials and you&apos;ve got your audio files ready, click Generate New Set to get a randomised set of kick, snare, and hi-hat sounds along with a track from your randomised Spotify playlists.&lt;/p&gt;
&lt;h2&gt;Why did I make this&lt;/h2&gt;
&lt;p&gt;I make beats a lot and I buy/download a lot of drum packs over the years. That means hundreds of drum packs get unused thanks to recency bias. A project like this can help me use drums I would never have used before, as well as shake things up with the samples I use.&lt;/p&gt;
&lt;h2&gt;Ideas and improvements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The code isn&apos;t great so feel free to critique it (but please be kind if you do; I&apos;m a self-taught Python coder!)&lt;/li&gt;
&lt;li&gt;I&apos;m also not great with Git so feel free to critique how I set up this repo (again, please be kind!)&lt;/li&gt;
&lt;li&gt;I have been working on having the sounds play a mini loop but I&apos;ve not included that in this repo.&lt;/li&gt;
&lt;li&gt;I guess you could replace Spotify playlists with YouTube videos or any other streaming platform with an API&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Morsel #17: Small and specific wins the AI race</title><link>https://lukealexdavis.co.uk/posts/undefined/</link><guid isPermaLink="true">https://lukealexdavis.co.uk/posts/undefined/</guid><pubDate>Thu, 27 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In my opinion, small, localised NLP projects are the best uses of AI (as an overarching discipline). Embeddings and classification are underrated building blocks that don&apos;t get talked about as much as general purpose LLMs because they&apos;re not sexy or &quot;useful&quot; to the wider public or B2B.&lt;/p&gt;
</content:encoded></item><item><title>Morsel #16: marimo</title><link>https://lukealexdavis.co.uk/posts/undefined/</link><guid isPermaLink="true">https://lukealexdavis.co.uk/posts/undefined/</guid><pubDate>Tue, 11 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import {YouTube} from &apos;astro-lazy-youtube-embed&apos;&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;yt-embed&quot;&amp;gt;
&amp;lt;YouTube title=&quot;An overview of marimo&quot; videoId=&quot;3N6lInzq5MI&quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://marimo.io/&quot;&gt;marimo&lt;/a&gt; is a new type of reactive notebook for Python which builds on the work you might have seen in &lt;a href=&quot;https://jupyter.org/&quot;&gt;Jupyter Notebook&lt;/a&gt; and &lt;a href=&quot;https://streamlit.io/&quot;&gt;Streamlit&lt;/a&gt; and combines them into an interesting package.&lt;/p&gt;
&lt;p&gt;As the site says, it&apos;s reproducible, integrates with popular progamming tools like &lt;a href=&quot;https://github.com/&quot;&gt;GitHub&lt;/a&gt; and SQL (which is built-in), and can either be used as a regular Python script or transformed into an interactive app whilst maintaining the code in the background.&lt;/p&gt;
&lt;p&gt;Just as an FYI: I&apos;m not sponsored or affiliated with marimo, I just love what I&apos;ve seen so far and wanted to share it here. I&apos;ve already started building some apps with it and they&apos;ve come in very handy. I&apos;m looking to refactor some other existing Jupyter Notebooks and Streamlit apps into marimo for ease of use, improve reactivity, and better real-time feedback so watch this space.&lt;/p&gt;
</content:encoded></item><item><title>Morsel #15: en-150</title><link>https://lukealexdavis.co.uk/posts/undefined/</link><guid isPermaLink="true">https://lukealexdavis.co.uk/posts/undefined/</guid><pubDate>Fri, 15 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;While snooping through a website&apos;s &amp;lt;code&amp;gt;hreflang&amp;lt;/code&amp;gt;, I noticed a unique ISO code: &amp;lt;code&amp;gt;en-150&amp;lt;/code&amp;gt;.&lt;/p&gt;
&lt;p&gt;According to a number of source, en-150 is the &lt;a href=&quot;https://www.w3.org/International/articles/language-tags/#region&quot;&gt;language subtag&lt;/a&gt; for English spoken in Europe, which comes in handy as a catch-all. While it is valid, I&apos;m not 100% that Google uses it as a hreflang value although on their old &lt;a href=&quot;https://web.archive.org/web/20250627152147/https://www.thinkwithgoogle.com/intl/en-emea/&quot;&gt;Think With Google page&lt;/a&gt; they use it as a data-* value (via &lt;a href=&quot;https://stackoverflow.com/questions/35989478/hreflang-tags-for-europe&quot;&gt;Stack Overflow&lt;/a&gt;). Here&apos;s one example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;link rel=&quot;alternate&quot; href=&quot;https://www.thinkwithgoogle.com/intl/en-emea/&quot; hreflang=&quot;en-dz&quot; data-code=&quot;en-emea&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this case, en-emea is used to cover English speakers in the EMEA area, which covers Europe, the Middle East and Africa.&lt;/p&gt;
&lt;p&gt;Knowing how data-* attributes work, I suspect this is more of a JS data thing and doesn&apos;t affect hreflang at all. But still interesting to see it used.&lt;/p&gt;
&lt;p&gt;If you know more about this or have spotted other examples, please let me know as I thought this was cool.&lt;/p&gt;
</content:encoded></item><item><title>Morsel #14: Textlist to array</title><link>https://lukealexdavis.co.uk/posts/undefined/</link><guid isPermaLink="true">https://lukealexdavis.co.uk/posts/undefined/</guid><pubDate>Mon, 07 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I have to change text lists into Python lists a lot and I always do it in Sublime Text with some regex. But I&apos;d rather have a quicker way to do it by pasting and clicking a button.&lt;/p&gt;
&lt;p&gt;So I made this! It takes a text list of strings and converts it into an array and then displays the array in a &amp;lt;code&amp;gt;pre&amp;lt;/code&amp;gt; tag.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Update - 7th Feb 2026&lt;/strong&gt;: so I realised today that this was susceptible to an XSS attack so I added a HTML sanitiser called &lt;a href=&quot;https://github.com/jitbit/HtmlSanitizer&quot;&gt;JS Html Sanitizer&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;form id=&quot;string-form&quot;&amp;gt;
&amp;lt;label for=&quot;strings&quot;&amp;gt;Enter your text strings, one per line:&amp;lt;/label&amp;gt;
&amp;lt;div&amp;gt;
&amp;lt;textarea type=&quot;text&quot; id=&quot;strings&quot; name=&quot;strings&quot; cols=&quot;80&quot; rows=&quot;10&quot; required&amp;gt;&amp;lt;/textarea&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;button type=&quot;submit&quot;&amp;gt;Generate array/list!&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div id=&quot;array-container&quot;&amp;gt;
&amp;lt;pre&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;script&amp;gt;
const form = document.querySelector(&apos;#string-form&apos;);
const arrayContainer = document.querySelector(&apos;#array-container pre&apos;);
form.addEventListener(&apos;submit&apos;, (e) =&amp;gt; {
e.preventDefault();
const stringVals = document.querySelector(&apos;#strings&apos;).value;
generateArrayText(stringVals);
});&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    // taken from https://stackoverflow.com/questions/23187013/is-there-a-better-way-to-sanitize-input-with-javascript
    function sanitiseString(str){
        str = str.replace(/[^a-z0-9áéíóúñü \.,_-]/gim,&quot;&quot;);
        return str.trim();
    }

    function generateArrayText(stringVals) {
        const stringsToArray = stringVals.split(&apos;\n&apos;)
        const sanitisedArray = stringsToArray.map(string =&amp;gt; HtmlSanitizer.SanitizeHtml(string))
        const arrayText = JSON.stringify(sanitisedArray);
        const displayText = document.createElement(&apos;displayed-text&apos;);
        arrayContainer.style.background = &quot;#111&quot;;
        arrayContainer.innerHTML = arrayText;
        arrayContainer.appendChild(displayText);
    }
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;script src=&quot;https://cdn.jsdelivr.net/npm/@jitbit/htmlsanitizer@latest/HtmlSanitizer.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;style&amp;gt;
#array-container {
margin: 1rem 0;
}
&amp;lt;/style&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Morsel #12: styling a WordPress post to look like a Bluesky post</title><link>https://lukealexdavis.co.uk/posts/undefined/</link><guid isPermaLink="true">https://lukealexdavis.co.uk/posts/undefined/</guid><pubDate>Thu, 23 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Even though I&apos;ve deleted Twitter, I miss just throwing a thought out into the ether. WordPress has &lt;a href=&quot;https://developer.wordpress.org/advanced-administration/wordpress/post-formats/&quot;&gt;post formats&lt;/a&gt; that can facilitate that. They are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;aside: like a note&lt;/li&gt;
&lt;li&gt;gallery: a gallery of images&lt;/li&gt;
&lt;li&gt;link: a link to another site (great for &lt;a href=&quot;/linkblog/&quot;&gt;linkblogs&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;image: a single image&lt;/li&gt;
&lt;li&gt;quote: a quote&lt;/li&gt;
&lt;li&gt;status: a status update, similar to a tweet&lt;/li&gt;
&lt;li&gt;video: a video or video playlist&lt;/li&gt;
&lt;li&gt;audio: an audio file or playlist&lt;/li&gt;
&lt;li&gt;chat: a chat transcript&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most of these I probably wouldn&apos;t much if at all but status intrigued me. I already use asides on all of my blogs front page but I wanted something different for those quick quips or musings. Because a lot of modern WordPress themes don&apos;t come with post formats out of the box, you have to enable them so I added this to my theme&apos;s functions.php file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;add_theme_support( &apos;post-formats&apos;, array( &apos;POST_FORMAT_NAME&apos; ) );
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then you have to create a template for your format(s) (unless your theme has them already) and that&apos;s where I had to get really creative. Here&apos;s what a Bluesky post looks like:&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;/images/bluesky-post.jpg&quot; alt=&quot;a bluesky post by me that says bleh&quot; loading=&quot;lazy&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;To get that working, I wrote a function in the template-tags.php file in my inc folder that retrieved my author name, my website (since it was also my Bluesky handle), and my avatar. All this info was accessible via the &lt;code&gt;get_the_author_meta()&lt;/code&gt; function:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function status_author() {
    $author_avatar = get_avatar( get_the_author_meta( &apos;ID&apos; ), 64 );
    $author_name = get_the_author_meta(&apos;user_firstname&apos;) . &apos; &apos; . get_the_author_meta(&apos;user_lastname&apos;);
    $url_cleanup = array(&quot;https://&quot; =&amp;gt; &quot;&quot;, &quot;/&quot; =&amp;gt; &quot;&quot;);
    $author_url = strtr(get_the_author_meta(&apos;url&apos;), $url_cleanup);
    echo &apos;&amp;lt;div class=&quot;status-author-meta&quot;&amp;gt;&apos; . &apos;&amp;lt;div class=&quot;status-author-meta-avatar&quot;&amp;gt;&apos; . $author_avatar . &apos;&amp;lt;/div&amp;gt;&apos; . &apos;&amp;lt;div class=&quot;status-author-meta-text&quot;&amp;gt;&apos; . &apos;&amp;lt;span&amp;gt;&apos; . $author_name. &apos;&amp;lt;/span&amp;gt;&apos; . &apos;&amp;lt;span&amp;gt;&apos; . &apos;@&apos; . $author_url . &apos;&amp;lt;/span&amp;gt;&apos; . &apos;&amp;lt;/div&amp;gt;&apos; . &apos;&amp;lt;/div&amp;gt;&apos;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I also added a bit to get rid of the URL scheme and all the slashes. Nice and neat! Then, I added the status_author() function to my template file (content-status.php):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php if ( is_single() ) : ?&amp;gt;
        &amp;lt;h1 class=&quot;entry-title&quot;&amp;gt;
            &amp;lt;?php print_post_title(); ?&amp;gt;
        &amp;lt;/h1&amp;gt;
&amp;lt;?php else : ?&amp;gt;
    &amp;lt;?php status_author(); ?&amp;gt;
    &amp;lt;div class=&quot;byline-text&quot; style=&quot;margin-bottom:1rem;&quot;&amp;gt;
        &amp;lt;?php the_content(); ?&amp;gt;                 
    &amp;lt;/div&amp;gt;
    &amp;lt;?php status_posted_on(); ?&amp;gt;
&amp;lt;?php endif; ?&amp;gt;

&amp;lt;?php if ( is_single() ) : ?&amp;gt;
    &amp;lt;br&amp;gt;
&amp;lt;?php endif; ?&amp;gt;

&amp;lt;?php 

if ( &apos;post&apos; === get_post_type() ) : ?&amp;gt;

&amp;lt;?php
endif; ?&amp;gt;
&amp;lt;/section&amp;gt;&amp;lt;!-- .entry-header --&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What this says is to check if the page is a &quot;single&quot;—like a blog post—and if it is, add the title, otherwise add the status author info, the status text, and the date it was posted. I added some CSS to make mimic the Bluesky post design:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* please don&apos;t roast my CSS, I&apos;m not a web dev!!! */

.status-author-meta, .status-author-meta-text, .status-author-meta-avatar {
    display: flex;
}

.status-author-meta {
    margin-bottom: 1rem;
}

.status-author-meta-text {
    flex-direction: column;
    justify-content: center;
    margin-left: 1rem;
    letter-spacing: 0.25px;
}

.status-author-meta-text span:first-child {
    font-size: 1.25rem;
    font-weight: 700;
}

.status-author-meta-text span:nth-child(2) {
    font-size: 1rem;
}

.status-author-meta-avatar img {
    border-radius: 50%;
}

.status-author-meta + .byline-text a {
    font-weight: 700;
    text-decoration: underline;
}

.status-author-meta + .byline-text a:hover {
    text-decoration: none;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And &amp;lt;span lang=&quot;fr&quot;&amp;gt;voilà&amp;lt;/span&amp;gt;!&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;/images/status-post.jpg&quot; alt=&quot;a wordpress status post that says &apos;imagining a world where SEGA still made hardware and they battled with Nintendo in the 2000s and 2010s for handheld dominance&apos;&quot; loading=&quot;lazy&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;Of course, how I put this together was specific to my setup and theme but the fundamental techniques are universal:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;add theme support for formats&lt;/li&gt;
&lt;li&gt;write the functions to get the author data&lt;/li&gt;
&lt;li&gt;output it in a template&lt;/li&gt;
&lt;li&gt;style it with CSS&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Reclaim your status updates!&lt;/p&gt;
</content:encoded></item><item><title>Morsel #11: how to check if a DOM element is in the viewport</title><link>https://lukealexdavis.co.uk/posts/undefined/</link><guid isPermaLink="true">https://lukealexdavis.co.uk/posts/undefined/</guid><pubDate>Wed, 22 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In my line of work, I struggle with finding DOM elements that cause issues but are otherwise hidden—by &lt;code&gt;display:none&lt;/code&gt;—or obscured by above-the-fold menus. I came across the latter the other day and the issue was lazy-loading. I wasn&apos;t sure whether the element classed as &quot;in the viewport&quot; despite not being immediately seen by the user unless they unfolded the menu.&lt;/p&gt;
&lt;p&gt;So I looked for a way to check and came across a JavaScript method called &lt;code&gt;getBoundingClientRect()&lt;/code&gt;. It returns a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/DOMRect&quot;&gt;DOMRect object&lt;/a&gt; that shows the size (width and height) and position (x and y coordinates) of an element in relation to the viewport.&lt;/p&gt;
&lt;p&gt;There are other ways to do it but this seemed the easiest for me (and coolest because I&apos;d never heard of it before). &lt;a href=&quot;https://www.geeksforgeeks.org/javascript/how-to-check-a-dom-element-is-visible-in-current-viewport/&quot;&gt;Here&apos;s an example taken from Geeks for Geeks&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function myfunction(value) {
    const item = value.getBoundingClientRect();
    return (
        item.top &amp;gt;= 0 &amp;amp;&amp;amp;
        item.left &amp;gt;= 0 &amp;amp;&amp;amp;
        item.bottom &amp;lt;= (
            window.innerHeight ||
            document.documentElement.clientHeight) &amp;amp;&amp;amp;
        item.right &amp;lt;= (
            window.innerWidth ||
            document.documentElement.clientWidth)
    );
}

const elementToCheck = document.getElementById(&apos;div1&apos;);

window.addEventListener(&apos;scroll&apos;, () =&amp;gt; {
    if (myfunction(elementToCheck)) {
        console.log(&apos;Element is visible in viewport&apos;);
    } else {
        console.log(&apos;Element is not visible in viewport&apos;);
    }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After I put my element in as the value of &lt;code&gt;elementToCheck&lt;/code&gt;, it came up as &quot;in the viewport&quot; and therefore shouldn&apos;t be lazy-loaded. Easy fix!&lt;/p&gt;
</content:encoded></item><item><title>Morsel #10: Ticking all the boxes</title><link>https://lukealexdavis.co.uk/posts/undefined/</link><guid isPermaLink="true">https://lukealexdavis.co.uk/posts/undefined/</guid><pubDate>Tue, 23 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I was trying to create a data report and needed to tick checkboxes to include certain information. Unfortunately, there were over 500 boxes and no option for selecting all of them so I Googled how to do it using JavaScript + the browser console.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stackoverflow.com/questions/386281/how-to-implement-select-all-check-box-in-html&quot;&gt;Stack Overflow came through for me&lt;/a&gt; and I thought I&apos;d share the answer here (thanks to &lt;a href=&quot;https://stackoverflow.com/users/8112776/ashleedawg&quot;&gt;ashleedawg&lt;/a&gt;):&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How to tick all checkboxes in your browser&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open your browser&apos;s console (the shortcut is usually Cmd/Ctrl+Shift+I (Chrome) or Cmd/Ctrl+Shift+I (Brave))&lt;/li&gt;
&lt;li&gt;Enter the following: &amp;lt;code&amp;gt;$(&quot;input:checkbox&quot;).attr(&apos;checked&apos;, true)&amp;lt;/code&amp;gt; (make sure to change &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt; for Firefox)&lt;/li&gt;
&lt;li&gt;Press Cmd/Ctrl+Enter to fire it and all the checkboxes should be checked!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I&apos;ve only tested this on one website on Brave and Firefox so feel free to email me if it doesn&apos;t work for you!&lt;/p&gt;
</content:encoded></item><item><title>Morsel #9: Oapy</title><link>https://lukealexdavis.co.uk/posts/undefined/</link><guid isPermaLink="true">https://lukealexdavis.co.uk/posts/undefined/</guid><pubDate>Thu, 23 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;My ninth morsel is a work one.&lt;/p&gt;
&lt;p&gt;Oapy is a web app that uses OpenAI&apos;s GPT models to generate &lt;a href=&quot;/jardim/tech/seo/&quot;&gt;SEO&lt;/a&gt; content at scale. That means anything from metadata (titles and descriptions) to category copy. But with the Playground mode (similar to OpenAI&apos;s Playground tool), you can generate any kind of written content.&lt;/p&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://oapy-impression.streamlit.app/&quot;&gt;Oapy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.impressiondigital.com/resources/tools/oapy/&quot;&gt;Impression guide to Oapy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Requirements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Internet access (no code necessary!)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;How to use&lt;/h2&gt;
&lt;p&gt;Follow the Impression guide above to find out how to use Oapy.&lt;/p&gt;
&lt;h2&gt;Python functions used&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/python-dicts/&quot;&gt;Dictionaries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/python-conditional-statements/&quot;&gt;If statements&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/defining-your-own-python-function/&quot;&gt;Functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://wiki.python.org/moin/ForLoop&quot;&gt;For-loops&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/python-lists-tuples/#python-lists&quot;&gt;Lists&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/list-comprehension-python/#using-list-comprehensions&quot;&gt;List comprehensions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Morsel #8: Bandcamp Album Length Calculator</title><link>https://lukealexdavis.co.uk/posts/undefined/</link><guid isPermaLink="true">https://lukealexdavis.co.uk/posts/undefined/</guid><pubDate>Sat, 04 Feb 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;My eighth morsel is a Python script that returns the running time of an album from Bandcamp in your CLI of choice. I previously used a JS plugin/applet in my browser but it stopped working and couldn&apos;t find a replacement (there probably is one but I also don&apos;t like installing add-ons unless I have to because Chromium browsers already hog enough memory!)&lt;/p&gt;
&lt;p&gt;Then I thought &quot;why not create a quick script to do the job?&quot;. I have also made this into a Streamlit app but that&apos;ll be coming in a future morsel. In the meantime, I discovered argparse and thought it&apos;d be handy for me to have everything run in my CLI and print out the album length. No frills!&lt;/p&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/starchildluke/bandcamp_calc&quot;&gt;GitHub link&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Requirements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.python.org/downloads/&quot;&gt;Python 3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Beautiful Soup&lt;/li&gt;
&lt;li&gt;Requests&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;How to use&lt;/h2&gt;
&lt;p&gt;In your command line, enter &lt;code&gt;python3 bandcamp_calc.py -u [URL]&lt;/code&gt; and it will print out the running time in seconds.&lt;/p&gt;
&lt;h2&gt;Python functions used&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/python-lists-tuples/#python-lists&quot;&gt;Lists&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/list-comprehension-python/#using-list-comprehensions&quot;&gt;List comprehensions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/python-for-loop/&quot;&gt;For loops&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/python-sum-function/&quot;&gt;sum&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.python.org/3/library/datetime.html&quot;&gt;datetime&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.python.org/3/library/argparse.html&quot;&gt;argparse&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Morsel #7: Random Pokémon Team Generator</title><link>https://lukealexdavis.co.uk/posts/undefined/</link><guid isPermaLink="true">https://lukealexdavis.co.uk/posts/undefined/</guid><pubDate>Sat, 23 Jul 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;/p&gt;
&lt;p&gt;My seventh morsel is a script that generate a random team based on all fully evolved Pokémon from Red/Blue/Yellow (excluding Legendaries and Mythicals).&lt;/p&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/starchildluke/random-pokemon-team&quot;&gt;GitHub link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://starchildluke-random-pokemon-team-random-gen1-team-kbwrud.streamlitapp.com/&quot;&gt;Streamlit app&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Requirements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.python.org/downloads/&quot;&gt;Python 3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://streamlit.io/&quot;&gt;streamlit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Python functions&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/python-lists-tuples/#python-lists&quot;&gt;Lists&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/list-comprehension-python/#using-list-comprehensions&quot;&gt;List comprehensions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.freecodecamp.org/news/python-find-in-list-how-to-find-the-index-of-an-item-or-element-in-a-list/&quot;&gt;&lt;code&gt;List.index()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/defining-your-own-python-function/&quot;&gt;Functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/python-json/&quot;&gt;JSON file/object handling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/python-conditional-statements/&quot;&gt;If statements&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://realpython.com/python-for-loop/&quot;&gt;For loops&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pynative.com/python-random-sample/&quot;&gt;&lt;code&gt;Random.sample()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Morsel #6: Federer in The Guardian</title><link>https://lukealexdavis.co.uk/posts/undefined/</link><guid isPermaLink="true">https://lukealexdavis.co.uk/posts/undefined/</guid><pubDate>Sun, 10 Jul 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;My sixth morsel is a quick script that scrapes The Guardian API for articles featuring &quot;Federer&quot; in the headline. This can, of course, be amended to search for any query in the API but I wanted some article to fill out my &lt;a href=&quot;/jardim/sport/roger-federer/&quot;&gt;Federer wiki page&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Requirements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.python.org/downloads/&quot;&gt;Python 3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://requests.readthedocs.io/en/latest/&quot;&gt;requests&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://open-platform.theguardian.com/&quot;&gt;An API key from The Guardian&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/starchildluke/federer-in-the-guardian&quot;&gt;GitHub link&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Morsel #5: Black fabric</title><link>https://lukealexdavis.co.uk/posts/undefined/</link><guid isPermaLink="true">https://lukealexdavis.co.uk/posts/undefined/</guid><pubDate>Fri, 08 Jul 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;My fifth morsel is called Black Fabric and it&apos;s a Python script that generates five randomised names that incorporate:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A fabric type as the first name&lt;/li&gt;
&lt;li&gt;A common surname of African diasporan descent (ie. Black folks from the UK, the US, Canada, the Caribbean, and other Central American countries whose ancestors were enslaved people)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Requirements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.python.org/downloads/&quot;&gt;Python 3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://streamlit.io/&quot;&gt;streamlit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/starchildluke/black_fabric&quot;&gt;GitHub link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://share.streamlit.io/starchildluke/black_fabric/main/black_fabric.py&quot;&gt;Streamlit app&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Credit to &lt;a href=&quot;https://twitter.com/yedoye_/status/1422264318079422466&quot;&gt;@yedoye&lt;/a&gt; for the idea and everyone in the replies.&lt;/p&gt;
</content:encoded></item><item><title>Morsel #4: bulk upload to Internet Archive with waybackpy</title><link>https://lukealexdavis.co.uk/posts/undefined/</link><guid isPermaLink="true">https://lukealexdavis.co.uk/posts/undefined/</guid><pubDate>Thu, 26 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;My fourth morsel is a way to backup your site to the Internet Archive.&lt;/p&gt;
&lt;h2&gt;Requirements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.python.org/downloads/&quot;&gt;Python 3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pypi.org/project/waybackpy/&quot;&gt;waybackpy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://advertools.readthedocs.io/en/master/&quot;&gt;advertools&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/starchildluke/wayback&quot;&gt;GitHub link&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I adapted some code from Koray Tuğberk GÜBÜR by using advertools to pull pages from a sitemap and &lt;code&gt;to_list()&lt;/code&gt; over &lt;code&gt;apply()&lt;/code&gt; and a &lt;code&gt;lambda&lt;/code&gt; function when extracting URLs to iterate over. Purely a preference thing.&lt;/p&gt;
&lt;p&gt;Also shout out to &lt;a href=&quot;https://github.com/eliasdabbas/advertools&quot;&gt;Elias Dabbas for the advertools library&lt;/a&gt; which has made sitemap handling in Python so much easier.&lt;/p&gt;
</content:encoded></item><item><title>Morsel #3: a custom font preload fix in WordPress</title><link>https://lukealexdavis.co.uk/posts/undefined/</link><guid isPermaLink="true">https://lukealexdavis.co.uk/posts/undefined/</guid><pubDate>Wed, 25 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;My third morsel is actually a WordPress fix.&lt;/p&gt;
&lt;p&gt;I decided to write this as I couldn&apos;t find the answer I was looking for myself and thought it might help anyone who had the same problem in the future.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: I realised the custom self-hosted fonts I was using on my WordPress sites weren&apos;t actually loading. I realised this after seeing the following message:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;The resource X was preloaded using link preload but not used within a few seconds from the window’s load event. Please make sure it has an appropriate ‘as’ value and it is preloaded intentionally.&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;That basically means the font loaded but it wasn&apos;t used &lt;em&gt;at all&lt;/em&gt;. I tried all the different things that Stack Overflow said but it still wouldn&apos;t work. Long story short: the @font-face declarations I was making in the main theme CSS file weren&apos;t acknowledged so I put them in the WordPress Customizer panel. To do that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Go to your Admin panel and then go to Appearance &amp;gt; Customize &amp;gt; Additional CSS&lt;/li&gt;
&lt;li&gt;Enter the appropriate @font-face code&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I used the following @font-face format:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@font-face {
  font-family: &apos;FontName&apos;;
  src: url(PATH) format(&apos;woff2&apos;);
  src: url(PATH) format(&apos;woff&apos;);
  font-weight: whatever that is;
  font-style: normal;
  font-display: swap;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In terms of the preload code, I recommend the following:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;link rel=&quot;preload&quot; href=&quot;PATH&quot; type=&quot;font/woff2&quot; as=&quot;font&quot; crossorigin=&quot;&quot;&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;I&apos;ve gone with woff2 only as it&apos;s widely supported and smaller in file size, which compounds the benefits of preloading for page speed. Just be careful of &lt;a href=&quot;https://fonts.google.com/knowledge/glossary/fout&quot;&gt;FOUT&lt;/a&gt; and set your &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control&quot;&gt;Cache-Control headers&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;And make sure to put that ahead of the &lt;code&gt;wp_head()&lt;/code&gt; in your header.php file so it can preload before the CSS (I believe that&apos;s how it works lol).&lt;/p&gt;
</content:encoded></item><item><title>Morsel #2: The YouTube Thumbnail Extractor</title><link>https://lukealexdavis.co.uk/posts/undefined/</link><guid isPermaLink="true">https://lukealexdavis.co.uk/posts/undefined/</guid><pubDate>Sun, 01 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;My second morsel is a Python script that extracts thumbnails from YouTube videos.&lt;/p&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/starchildluke/yt_thumbnail&quot;&gt;GitHub link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://share.streamlit.io/starchildluke/yt_thumbnail/main/yt_st.py&quot;&gt;Streamlit app&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Requirements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.python.org/downloads/&quot;&gt;Python 3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://requests.readthedocs.io/en/latest/&quot;&gt;requests&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://streamlit.io/&quot;&gt;streamlit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Add the URL to extract the thumbnail from and it will display it at the bottom. Currently only works if you put in the full YouTube video URL without any parameters (so not youtu.be or ?t=8 for example). Again, open to suggestions on how to make it work with any valid YouTube URL (as I&apos;m not so great with &lt;a href=&quot;https://docs.python.org/3/library/re.html&quot;&gt;re&lt;/a&gt; yet)&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
</content:encoded></item><item><title>Morsel #1: The Tag Suggester</title><link>https://lukealexdavis.co.uk/posts/undefined/</link><guid isPermaLink="true">https://lukealexdavis.co.uk/posts/undefined/</guid><pubDate>Fri, 15 Apr 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;My first morsel is a Python script that extracts all the tags from my blogs and creates randomised Google search URLs for content ideation.&lt;/p&gt;
&lt;h2&gt;Requirements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.python.org/downloads/&quot;&gt;Python 3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://requests.readthedocs.io/en/latest/&quot;&gt;requests&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://streamlit.io/&quot;&gt;streamlit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/starchildluke/tag_suggester/&quot;&gt;GitHub link&lt;/a&gt;
&lt;a href=&quot;https://share.streamlit.io/starchildluke/tag_suggester/main/tagsuggester.py&quot;&gt;Streamlit app&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Example from &lt;a href=&quot;https://cultrface.co.uk&quot;&gt;Cultrface&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://google.com/search?q=martin+luther+king+soup&quot;&gt;https://google.com/search?q=martin+luther+king+soup&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As they are randomised, some of the results are more nonsense than anything but you never know. That&apos;s the beauty of it—you could end up down a rabbit hole you never knew existed.&lt;/p&gt;
&lt;p&gt;Feel free to fork the repository and make your own version (subject to the AGPL-3.0 License)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UPDATE&lt;/strong&gt;: So this script doesn&apos;t update the JSON files containing all the blog tags as I had hoped. Still looking into how to do that but if anyone reading this knows how to update a JSON file via a Streamlit app hosted on GitHub, I&apos;d love to know.&lt;/p&gt;
</content:encoded></item></channel></rss>